文章目录
1.I/O复用简介
I/O复用指定是进程需要具备一种告知内核的能力:使得内核发现进程指定的一个或多个I/O条件就绪(即输入或输出已经准备好被读取,或者描述符已经能承载更多的输出),它就通知进程。这个能力是通过select和poll函数来支持的。
I/O复用典型的应用场景:
- 当客户处理多个描述符(通常为交互式输入与网络套接字)时
- 当一个客户同时处理多个套接字
- 如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字
- 如果一个服务器既要处理TCP,又要处理UDP
- 如果一个服务器要处理多个服务或者多个协议
2.I/O模型
- 阻塞式
- 非阻塞式
- I/O复用
- 信号驱动
- 异步I/O
3.select函数的使用
3.1函数原型
#include<sys/select.h>
int select(int nfds, fd_set *restrict readfds,fd_set *restrict writefds, fd_set *restrict errorfds,
struct timeval *restrict timeout);
3.2 返回值
- 负值:select错误
- 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
3.3传入参数
- int n:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
- fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
- fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
- fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
- struct timeval *timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。如果参数timeout设为NULL则表示select()没有timeout。
3.4如何设置传入参数fd_set
#include<sys/select.h>
fd_set allFd;//定义fd_set变量
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的全部位
3.5select使用
- 创建fd_set类型的变量。
- 利用FD_ZERO清除变量set的全部位
- 利用FD_SET设置想要检测的描述符
- 阻塞于select
- select返回后,根据其返回值判断有几个描述符应该处理,然后利用FD_ISSET测试哪些描述符需要处理,并做相应的处理
- 如果上一步处理完后,不在需要检测此描述符,则利用FD_CLR清除该描述符
- 返回第三步
3.6错误代码
执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数n为负值。
ENOMEM 核心内存不足
4.poll函数的使用
poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。
4.1函数原型
#include <sys/poll.h>
int poll(struct pollfd fd[], nfds_t nfds, int timeout);
4.2参数说明
- 第一个参数:一个结构数组,struct pollfd结构如下:
struct pollfd{
int fd; //文件描述符
short events; //请求的事件
short revents; //返回的事件
};
events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。
poll函数的事件标志符值
常量 | 说明 |
---|---|
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
POLLERR | 发生错误 |
POLLHUP | 发生挂起 |
POLLNVAL | 描述字不是一个打开的文件 |
注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。
- 第二个参数nfds:要监视的描述符的数目。
- 最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。