内容简介:IO复用:让进程等待一系列IO条件而不是一个IO条件通过定义:
IO复用:让进程等待一系列IO条件而不是一个IO条件
通过 select
和 poll
函数我们可以同时监听多个描述符,在描述符就绪时进行对应的操作。
select
定义:
//maxfdpl: 待测试的描述符个数 //返回就绪描述符的个数,若超时则为0, 若出错则为-1 int select(int maxfdpl, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); //超时选项 //NULL:wait forever;0:don't wait struct timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; //每个fds_bit的每一位对应一个描述符 typedef struct fd_set { int fds_bits[FD_SETSIZE/sizeof(int)/NBBY]; /* NBBY=bits in a byte ; usually 8*/ } fd_set; #define FD_SETSIZE 1024 /* fd_set中描述符的总数 */ void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */ void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */ void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */ void FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */ 复制代码
select的使用方法:
int fds[FD_SETSIZE]; 保存当前所有描述符 fd_set rset, wset, eset; //定义读、写、异常对应的fd_set //初始化fd_set,非常重要且不能省略,因为如果不初始化可能会影响FD_ISSET的调用结果 FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); for (;;) { //在循环中调用select select(FD_SETSIZE, &rset, &wset, &eset, NULL); //遍历当前所有的fd,处理就绪的fd for (int i = 0; i < FD_SETSIZE; i++) { if (FD_ISSET(fds[i], &rset)) { //handle read } if (FD_ISSET(fds[i], &wset)) { //handle write } if (FD_ISSET(fds[i], &eset)) { //handle exception } } } 复制代码
fd_set的限制
在很早之前就看到网上的介绍说select在描述符个数上是有限制的,现在终于知道这个限制是从哪来的了,这实际上跟fd_set的实现机制有关。
fd_set
中使用int数组中的各个位来保存多个描述符的状态,这个数组称为 描述符集
,比如数组的第一个数有32位,那么第一个数的每一位就表示第0~31个描述符的状态,这样一来当我们调用 FD_ISSET
来判断某一个描述符状态时,我们只需要找到其对应的位判断其是0或者1就行了;同理当我们需要设置某个描述符状态时,只需要设置对应的位的状态即可。而 fd_set
中数组的大小是通过 FD_SETSIZE
这个值算出来的, FD_SETSIZE
是一个宏定义,通常它的默认值比较小,在我的mac上查看其默认值是1024,也就是说在我的mac上select能够支持的最大的描述符数量是1024个。当然 FD_SETSIZE
也可以重新定义,但如果要调整需要重新编译内核。
描述符读就绪条件
- 接收缓冲区数据字节数大于低水位(默认是1),这时读取操作返回大于0
- 读半关闭,也就是对端发来了FIN,这时返回0,也就是EOF
- 当前套接字是监听套接字,而且已完成连接数不为0,这时可以进行accept操作
- 描述符上有套接字错误需要处理
描述符写就绪条件
- 发送缓冲区数据字节数大于低水位(通常为2048);
- 套接字已连接或不需要连接(UDP)
-
写半关闭,这时如果再写会收到
SIGPIPE
信号 -
使用非阻塞式
connect
的套接字已建立连接或者connect
失败 - 描述符上有套接字错误需要处理
shutdown&close
有两个函数可以关闭套接字: shutdown
和 close
,它们的区别如下:
-
close
会将引用计数减1,当计数为0时关闭套接字;shutdown
可以直接触发关闭。 -
close
会终止读和写两个方向;shutdown
可以通过参数howto
指定关闭某个方向
int close(int fd); int shutdown(int fd, int howto); /* * howto arguments for shutdown(2), specified by Posix.1g. */ #define SHUT_RD 0 /* shut down the reading side */ #define SHUT_WR 1 /* shut down the writing side */ #define SHUT_RDWR 2 /* shut down both sides */ 复制代码
poll
poll
和 select
的功能类似,也支持IO复用,但是 poll
没有使用描述符集,而是使用 pollfd
这种结构来表示描述符的状态。
//nfds:array的长度,受进程能打开的最大文件数限制 //返回就绪描述符的个数,若超时则为0, 若出错则为-1 int poll(struct pollfd *fdarray, unsigned long nfds, int timeout); struct pollfd { int fd; /* descriptor to check */ short events; /* events of intrests on fd */ short revents; /* events that occurred on fd */ }; 复制代码
poll
的使用方法:
struct pollfd pollfds[OPEN_MAX]; //定义pollfd数组,将需要监听的描述符保存起来 for (;;) { //在循环中调用poll poll(pollfds, OPEN_MAX, INFTIM); for (int i = 0; i < OPEN_MAX; i++) { //遍历pollfd数组处理就绪的描述符 struct pollfd pfd = pollfds[i]; if (pfd.revents & POLLIN) { //handle read } if (pfd.revents & POLLOUT) { //handle write } } } 复制代码
poll
识别的数据类型:普通(normal)、优先级带(priority band)、高优先级(high priority);
这些术语来自基于流的实现。(没太明白,先标记下)
events常量列举:
常量 | 出现在events | 出现在revents | 说明 |
---|---|---|---|
POLLIN | y | y | 普通或优先级带数据可读 |
POLLRDNORM | y | y | 普通数据可读 |
POLLRDBAND | y | y | 优先级带数据可读 |
POLLPRI | y | y | 高优先级带数据可读 |
POLLOUT | y | y | 普通数据可写 |
POLLWRNORM | y | y | 普通数据可写 |
POLLWRBAND | y | y | 优先级带数据可写 |
POLLERR | n | y | 发生错误 |
POLLHUP | n | y | 发生挂起 |
POLLNVAL | n | y | 描述符不是一个打开的文件 |
poll的就绪条件:
- 所有正规TCP数据和所有UDP数据视为普通数据
- TCP带外数据视为优先级带数据
- 当TCP读半关闭时(收到对端传来的FIN),也视为普通数据,随后的读操作将返回0
-
TCP连接存在错误既可以视为普通数据,也可以视为错误(
POLLERR
)。随后的读操作将返回-1,并设置全局的errno
变量。 - 监听套接字上有新的连接既可以视为普通数据,也可以视为优先级数据。
-
非阻塞式的
connect
的完成视为使对应的套接字可写。
总结
select
和 poll
都支持IO复用,其思路都是调用函数监听多个描述符,当有描述符就绪或者超时的时候函数调用就会返回,对应的描述符集合状态也会改变,这时候再遍历描述符集合,处理其中就绪的部分即可。
这种方式在需要监听的描述符比较小,或者是每次就绪的描述符很多的情况下比较有效;但当描述符很多而且每次只有少数描述符就绪时,效率就比较低了。后面出现的 epoll
就避免了这种线性遍历的问题。
另外 select
还受 FD_SETSIZE
的限制,只能处理较少的描述符,而 poll
则没有这个限制。 poll
监听的集合大小只受进程能打开的文件数量( RLIMIT_NOFILE
)的限制。
以上所述就是小编给大家介绍的《《UNIX网络编程》笔记 - select和poll》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Swift语言实战入门
伍星、罗飞、刘志华、王浩力、刘蕾 / 人民邮电出版社 / 2014-10-23 / 79
《Swift语言实战入门》以Swift语言的基础知识和实战技巧为主要内容,佐以大量的实例和图片进行讲解。全书内容分为三大部分,共11章节。第一大部分讲述Swift语言的基础知识和语法,第二大部分讲解开发框架和库的相关内容,第三大部分集中讲解以2048游戏为例的实战演练,从入门到实战层层递进。本书注重实战,秉承着学以致用的原则,让读者真正看后能够实际操作。120个代码清单全部共享,配套教学视频在线收......一起来看看 《Swift语言实战入门》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
在线进制转换器
各进制数互转换器