1.I/O多路转接之select
1.1.select初识
select是系统提供的一个多路转接接口。
• select系统调用可以让我们的程序同时监视多个文件描述符的上的事件是否就绪。
• select的核心工作就是等,当监视的多个文件描述符中有一个或多个事件就绪时,select才会成功返回并将对应文件描述符的就绪事件告知调用者。
• 事件就绪也就是文件描述符的状态发生变化,即可读与不可读之间转变、可写与不可写之间转变、正常与异常之间转变。
1.2.select函数
参数:
nfds:需要监视的文件描述符中,最大的文件描述符值+1。
readfds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的读事件是否就绪,返回时内核告知用户哪些文件描述符的读事件已经就绪。
writefds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的写事件是否就绪,返回时内核告知用户哪些文件描述符的写事件已经就绪。
exceptfds:输入输出型参数,调用时用户告知内核需要监视哪些文件描述符的异常事件是否就绪,返回时内核告知用户哪些文件描述符的异常事件已经就绪。
timeout:输入输出型参数,调用时由用户设置select的等待时间,返回时表示timeout的剩余时间。
参数timeout的取值:NULL/nullptr:select调用后进行阻塞等待,直到被监视的某个文件描述符上的某个事件就绪。
0:selec调用后t进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,select检测后都会立即返回。
特定的时间值:select调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在该时间后select进行超时返回。
返回值:如果函数调用成功,则返回有事件就绪的文件描述符个数。
如果timeout时间耗尽,则返回0。
如果函数调用失败,则返回-1,同时错误码会被设置。
select调用失败时,错误码可能被设置为:EBADF:文件描述符为无效的或该文件已关闭。
EINTR:此调用被信号所中断。
EINVAL:参数nfds为负值。
ENOMEM:核心内存不足。
注:使用select函数需要包含<sys/select.h>头文件。
fd_set结构:
fd_set结构与sigset_t结构类似,fd_set本质也是一个位图,用位图中对应的位来表示要监视的文件描述符。
调用select函数之前就需要用fd_set结构定义出对应的文件描述符集,然后将需要监视的文件描述符添加到文件描述符集当中,这个添加的过程本质就是在进行位操作,但是这个位操作不需要用户自己进行,系统提供了一组专门的接口,用于对fd_set类型的位图进行各种操作。
如下:
void FD_CLR(int fd, fd_set *set); //用来清除描述词组set中相关fd的位 int FD_ISSET(int fd, fd_set *set); //用来测试描述词组set中相关fd的位是否为真 void FD_SET(int fd, fd_set *set); //用来设置描述词组set中相关fd的位 void FD_ZERO(fd_set *set); //用来清除描述词组set的全部位
timeval结构:
传入select函数的最后一个参数timeout,就是一个指向timeval结构的指针,timeval结构用于描述一段时间长度,该结构当中包含两个成员,其中tv_sec表示的是秒,tv_usec表示的是微秒。
1.3.socket就绪条件
读就绪:
• socket内核中,接收缓冲区中的字节数,大于等于低水位标记
SO_RCVLOWAT
,此时可以无阻塞的读取该文件描述符,并且返回值大于0。• socket TCP通信中,对端关闭连接,此时对该socket读,则返回0。
• 监听的socket上有新的连接请求。
• socket上有未处理的错误。
写就绪:
• socket内核中,发送缓冲区中的可用字节数,大于等于低水位标记SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0。
• socket的写操作被关闭(close或者shutdown),对一个写操作被关闭的socket进行写操作,会触发SIGPIPE信号。
• socket使用非阻塞connect连接成功或失败之后。
• socket上有未读取的错误。
异常就绪:
• socket上收到带外数据。
注:带外数据和TCP的紧急模式相关,TCP报头当中的URG标志位和16位紧急指针搭配使用,就能够发送/接收带外数据。