Использование функции выбора IO-мультиплексирования под Linux

Роль функции выбора:

    Если в нашей программе есть два места, которые нужно заблокировать, например, чтение данных с сервера и одновременное чтение данных с клавиатуры (если не использовать блокировку и запросы, это потребует много системных ресурсов) . В настоящее время у нас есть две блокировки, вы, конечно , можете использовать многопоточный или многопроцессорный метод для решения , но сегодня мы представляем другой метод: мультиплексирование ввода / вывода, такое как выбор, опрос и другие механизмы, выбор которого можно отслеживать Нам нужно следить за изменениями в файле дескриптора - чтение или запись или ненормальное.

头文件:
#include <sys/select.h>

函数原型:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

Параметры:

1.int maxfdp является целочисленным значением, которое относится к диапазону всех файловых дескрипторов в коллекции, то есть максимальное значение всех файловых дескрипторов плюс 1 , не может быть неправильным! В Windows значение этого параметра не имеет значения и может быть установлено неправильно.
2. fd_set * readfds - указатель на структуру fd_set, этот набор должен включать файловые дескрипторы, мы должны отслеживать изменения чтения этих файловых дескрипторов, то есть нас беспокоит вопрос о том, можем ли мы читать данные из этих файлов , если это В коллекции есть файл, доступный для чтения, select вернет значение больше 0, указывая, что файл доступен для чтения, если нет доступного для чтения файла, а затем в соответствии с параметром timeout, чтобы определить, следует ли превышать тайм-аут, если время превышает timeout, select возвращает 0, Если возникает ошибка, возвращается отрицательное значение. Вы можете ввести значение NULL, означающее, что вам не нужны какие-либо изменения при чтении файлов.
3. fd_set * writefds - указатель на структуру fd_set, этот набор должен включать файловые дескрипторы, мы должны отслеживать изменения записи этих файловых дескрипторов, то есть нас беспокоит вопрос о том, можем ли мы записывать данные в эти файлы , если это Если в коллекции есть файл, который можно записать, select вернет значение больше 0. Это означает, что существует файл, который можно записать. Если нет файла, который можно записать, будет определено, истекло ли оно в соответствии с тайм-аутом. Отрицательное значение возвращается при возникновении ошибки. Вы можете передать значение NULL, чтобы указать, что вас не волнуют какие-либо изменения записи файла.
4.fe_set * errorfds соответствует назначению двух вышеупомянутых параметров, используемых для отслеживания исключений ошибок файла.
5. struct timeval * timeout - это тайм-аут выбора , этот параметр очень важен, он может делать выбор в трех состояниях.
      Первое: если в качестве формального параметра передается NULL, то есть не передается временная структура, выборка переводится в заблокированное состояние, Необходимо подождать, пока дескриптор файла не изменится в наборе дескрипторов файла мониторинга, во-
      вторых: если значение времени установлено на 0 секунд 0 миллисекунд, оно становится чисто неблокирующей функцией, независимо от того, изменился ли дескриптор файла, Все возвращаются немедленно для продолжения выполнения, файл возвращает 0, если изменений нет, и положительное значение, если есть изменения, в-
      третьих: значение тайм-аута больше 0, это период ожидания ожидания, то есть выбор блокируется в течение периода ожидания, а событие наступает в течение периода ожидания Он возвращает, в противном случае он вернется независимо от того, что после тайм-аута, возвращаемое значение будет таким же, как указано выше

Возвращаемое значение:

  Отрицательное значение: выберите ошибку;
  положительное значение: некоторые файлы могут быть прочитаны и записаны или ошибка;
  0: ожидание тайм-аута, нет файла, который можно прочитать , записать или ошибиться


Код ошибки:

Если выполнение выполнено успешно, возвращается число файловых дескрипторов, состояние которых изменилось. Если возвращается 0, это означает, что истек период ожидания до изменения состояния дескриптора. Если возникает ошибка, возвращается -1. Причина ошибки сохраняется в errno. Значения writefds, кроме fds и timeout становятся непредсказуемыми.
 

EBADF 文件描述词为无效的或该文件已关闭

EINTR 此调用被信号所中断

EINVAL 参数n为负值。

ENOMEM 核心内存不足

Описание функции:

select () используется для ожидания изменения состояния дескриптора файла. Параметр maxfdp представляет наибольший дескриптор файла плюс 1, а параметры readfds, writefds и exceptionfds называются дескриптивными фразами и используются для возврата состояния чтения, записи или исключений в дескриптор. Макрос ниже предоставляет способ обработки этих трех фраз описания:

FD_CLR(inr fd,fd_set* set);		用来清除描述词组set中相关fd的位

FD_ISSET(int fd,fd_set *set);	用来测试描述词组set中相关fd的位是否为真

FD_SET(int fd,fd_set*set);		用来设置描述词组set中相关fd的位

FD_ZERO(fd_set *set);			用来清除描述词组set的全部位

Описание структуры:

1) struct fd_set может пониматься как набор, который хранит файловые дескрипторы (filedescriptors), то есть файловые дескрипторы, которые могут быть тем, что мы называем файлами обычного значения, конечно, любое устройство, конвейер, FIFO и т. Д. В Unix являются Он находится в форме файла, все включено, поэтому нет сомнений, что сокет - это файл, а дескриптор сокета - дескриптор файла.
Коллекцией fd_set можно манипулировать вручную некоторыми макросами, такими как
 

清空集合FD_ZERO(fd_set *);
将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set*);
将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*);
检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。

2) struct timeval - это структура, обычно используемая всеми для представления значений времени. Есть два члена, один - секунды, а другой - тонкие числа. Как показано ниже:

truct timeval
{
	time_t tv_sec;		//seconds 秒
	time_t tv_usec;		//microseconds 微秒,1000000 微秒 = 1秒
}; 
//头文件
#include <sys/time.h>

Примечание:

1. Параметры каждого дескриптора файла fd_set, установленного в функции select (), передали изменения до и после select ():

До: Указывает коллекцию файловых дескрипторов, представляющих интерес

После: сбор данных (если не возвращен в течение тайм-аута)

Другими словами: сначала мы устанавливаем файловый дескриптор каждого ввода-вывода для отслеживания в коллекцию fd_set, затем вызываем select (), и, наконец, коллекция fd_set имеет только «ненормальные» (включая чтение, запись и ненормальные) файлы Дескриптор, поэтому шаги, упомянутые выше, чтобы установить коллекцию fd_set, вызвать select () и определить, какой IO имеет данные, обычно реализуются в цикле while.

2. Изменения, отправленные коллекцией fd_set до и после вызова функции select, выполняются ядром.

Пример 1. Есть ли данные, поступающие на клавиатуру мониторинга под Linux?

#include <sys/time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#include <unistd.h>
#include <iostream>
using namespace std;

int main ()
{
    int keyboard;
    int ret,i;
    char c;
    fd_set readfd;
    struct timeval timeout;
    keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
    assert(keyboard>0);
    while(1)
    {
        timeout.tv_sec=1;
        timeout.tv_usec=0;
        FD_ZERO(&readfd);                //结构体清0
        FD_SET(keyboard,&readfd);        //把键盘文件的句柄加入"读集合"之中
 
        ///监控函数
        ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);    //第一个参数为最大文件句柄+1
        if(ret == -1)   //错误情况
            cout<<"error"<<endl ;
        else if(ret)    //返回值大于0 有数据到来
            if(FD_ISSET(keyboard,&readfd))    //检查集合中键盘文件是否可以读(因为加入"读集合"的可能不止键盘)
            {            
                /* 读出键盘的数据 */
                i=read(keyboard,&c,1);
                if('\n'==c)
                    continue;
                printf("hehethe input is %c\n",c);
                if ('q'==c)
                    break;
            }
        else    //超时情况
        {
            cout<<"time out"<<endl;
            continue;
        }
    }
}

 

Пример 2. Мониторинг нескольких файлов для чтения в Linux (псевдокод)?

1.    
    fd_set tRFds;            //定义fd_set类型结构体的变量tRFds
    FD_ZERO(&tRFds);		//结构体先清0
    int g_iMaxFd = -1;        //定义g_iMaxFd记录 最大句柄+1
2.
    比较要监控的各个文件的句柄,取出最大值再加1赋给g_iMaxFd
    if(Fd1 > Fd2)
    {
        g_iMaxFd = Fd1+1;
    }
    else
    {
        g_iMaxFd = Fd2+1;
    }
3.
    FD_SET(Fd1, &tRFds);	//把Fd1、Fd2加入"读集合"之中
    FD_SET(Fd2, &tRFds);    //Fd1和Fd2为两个不同的文件句柄
    ...
4.
    iRet = select(g_iMaxFd, &tRFds, NULL, NULL, NULL);
	if (iRet > 0)			//返回正数说明有数据可读了
	{
		
		if (FD_ISSET(Fd1, &tRFds))        //是文件1可读
		{
			Fd1_read(...);
		}

        else if(FD_ISSET(Fd2, &tRFds))      //是文件2可读
        {    
            Fd2_read(...);  
        }	

        ...
	}

Основная ссылка на эту статью: https://blog.csdn.net/mayue_web/article/details/89021273

Опубликовано 42 оригинальных статей · Мне нравится 10 · Посетителей 10 000+

рекомендация

отblog.csdn.net/qq_37659294/article/details/104263105