Роль функции выбора:
Если в нашей программе есть два места, которые нужно заблокировать, например, чтение данных с сервера и одновременное чтение данных с клавиатуры (если не использовать блокировку и запросы, это потребует много системных ресурсов) . В настоящее время у нас есть две блокировки, вы, конечно , можете использовать многопоточный или многопроцессорный метод для решения , но сегодня мы представляем другой метод: мультиплексирование ввода / вывода, такое как выбор, опрос и другие механизмы, выбор которого можно отслеживать Нам нужно следить за изменениями в файле дескриптора - чтение или запись или ненормальное.
头文件:
#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