Use of select function of IO multiplexing under Linux

The role of select function:

    If there are two places in our program that need to be blocked, such as reading data from the server and reading data from the keyboard at the same time (if not using blocking and querying, it will take up a lot of system resources) . At this time we have two blocking, you can of course use multi-threaded or multi-process method to solve , but we introduce another method today: I / O multiplexing, such as select, poll and other mechanisms, select it can monitor We need to monitor the changes in the file descriptor-read or write or abnormal.

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

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

parameter:

1.int maxfdp is an integer value, which refers to the range of all file descriptors in the collection, that is , the maximum value of all file descriptors plus 1 , can not be wrong! In Windows, the value of this parameter does not matter and can be set incorrectly.
2. fd_set * readfds is a pointer to the fd_set structure, this set should include file descriptors, we are to monitor the read changes of these file descriptors, that is, we are concerned about whether we can read data from these files , if this There is a file readable in the collection, select will return a value greater than 0, indicating that there is a file readable, if there is no readable file, then according to the timeout parameter to determine whether to timeout, if the time exceeds the timeout, select returns 0, If an error occurs, a negative value is returned. You can enter a NULL value, indicating that you do not care about any file read changes.
3. fd_set * writefds is a pointer to the fd_set structure, this set should include file descriptors, we are to monitor the write changes of these file descriptors, that is, we are concerned about whether we can write data to these files , if this If there is a file in the collection that can be written, select will return a value greater than 0, indicating that there is a file that can be written. If there is no file that can be written, the timeout will be determined according to the timeout. If the time exceeds the timeout, select returns 0. A negative value is returned when an error occurs. You can pass in a NULL value to indicate that you do not care about any file write changes.
4.fe_set * errorfds is the same as the intent of the above two parameters, used to monitor file error exceptions.
5. struct timeval * timeout is the timeout of select , this parameter is very important, it can make select in three states.
      First: If NULL is passed in as a formal parameter, that is, no time structure is passed in, the select is placed in a blocked state, Must wait until a file descriptor changes in the monitoring file descriptor set;
      second: if the time value is set to 0 seconds 0 milliseconds, it becomes a pure non-blocking function, regardless of whether the file descriptor has changed, All return immediately to continue execution, the file returns 0 if there is no change, and a positive value when there is a change;
      third: the value of timeout is greater than 0, this is the timeout period of waiting, that is, the selection is blocked within the timeout period, and an event arrives within the timeout period It returns, otherwise it will return no matter what after the timeout, the return value is the same as above.

return value:

  Negative value: select error;
  positive value: some files can be read and written or error;
  0: wait for timeout, there is no file that can be read , written or wrong.


error code:

If the execution is successful, it returns the number of file descriptors whose status has changed. If it returns 0, it means that the timeout period has elapsed before the descriptor status changed. If an error occurs, it returns -1. The cause of the error is stored in errno. The values ​​of writefds, exceptfds and timeout become unpredictable.
 

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

EINTR 此调用被信号所中断

EINVAL 参数n为负值。

ENOMEM 核心内存不足

Function description:

select () is used to wait for the state of the file descriptor to change. The parameter maxfdp represents the largest file descriptor plus 1, and the parameters readfds, writefds, and exceptfds are called descriptive phrases and are used to return the status of reading, writing, or exceptions to the descriptor. The macro below provides a way to handle these three description phrases:

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的全部位

Structure description:

1) struct fd_set can be understood as a set, which stores file descriptors (filedescriptors), that is, file handles, which can be what we call files of ordinary meaning, of course, any device, pipeline, FIFO, etc. under Unix are It is in the form of a file, all included, so there is no doubt that a socket is a file and a socket handle is a file descriptor.
The fd_set collection can be manipulated manually by some macros, such as
 

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

2) struct timeval is a structure commonly used by everyone to represent time values. There are two members, one is seconds and the other is subtle numbers. As follows:

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

note:

1. The parameters of each file descriptor fd_set set in the select () function have sent changes before and after select ():

Before: Indicates the collection of file descriptors of interest

After: a collection of data (if not returned in a timeout)

In other words: we first set the file descriptor of each I / O to be monitored to the fd_set collection, then call select (), and finally the fd_set collection only has "abnormal" (including read, write, and abnormal) files Descriptor, so the steps mentioned above to set the fd_set collection, call select (), and determine which IO has data are usually implemented in a while loop.

2. The changes sent by the fd_set collection before and after calling the select function are done by the kernel.

Example 1: Is there data coming on the monitoring keyboard under 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;
        }
    }
}

 

Example 2: Is monitoring multiple files readable under Linux (pseudo code)?

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(...);  
        }	

        ...
	}

The main reference of this article: https://blog.csdn.net/mayue_web/article/details/89021273

Published 42 original articles · Like 10 · Visitors 10,000+

Guess you like

Origin blog.csdn.net/qq_37659294/article/details/104263105