笔记分两部分,第一部分是Mannual中对select的重要段落的翻译,第二部分参考了博客,相关链接已在正文中给出。
Linux Programmer’s Manual
Description
select() 干了什么事情
select() 允许一个program监控多个 file descriptor,一直等到一个或者多个file descriptors对某类I/O操作变得ready(例如,input possible)
一个file descriptor被认为是ready的,如果它可以执行相应的I/O操作(如,read without blocking, or a sufficiently small write)
select() 只能监控file descriptors numbers小于FD_SETSIZE的那些file descriptors;poll没有这个限制
三个独立的sets of file descriptors被监控。列举在readfds中的file descriptors被监控,看characters变得available for reading(更精确的说,看是否read不会被block;特别的,一个file descriptor在end-of-file上也是ready的)。这个位于writefds中的file descriptor会被监控,看是否有space可以write(虽然,一个large write 也可能是block的)。位于exceptfds中的file descriptors会被监控,对那些exceptional conditions。(exceptional conditions的例子,可以查看在poll2中关于POLLPRI的讨论)
select() 退出的时候会有什么操作
select()退出的时候,每个file descriptor sets会在正确的位置被修改,以表明哪个file descriptor实际改变了状态。(于是,如果在一个loop中使用select()函数,这三类sets必须在每次select()被调用前重新初始化)
三类file descriptors sets中的任一file descriptor set都可以被指定为NULL,如果相应类别的事件没有file descriptors需要被监控的话。
对file descriptors sets进行操作所使用的macros
四个macros用于对sets进行操作。
- FD_ZERO() 清空set
- FD_SET() 给set add 一个file descriptor
- FD_CLR() 给set remove 一个file descriptor
- FD_ISSET() 测试一个file descriptor是否是某个set的元素,这个macro在select()返回之后有用。
nfds参数的描述
nfds应该被设置为highest-numbered file descriptor in any of the three sets,再加上1.
检查每个集合中指定的文件描述符,直到该限制为止。
Return value
成功的话,select()返回包含在三个returned descriptor sets的file descriptors的数量(就是,在readfds, writefds, exceptfds中被设置的bits的总数量),如果超时了的话,这个值可以是0。
失败的话:
- -1将被返回
- errno被设置来标识这个错误
- file descriptor sets are unmodified
- timeout becomes undefined
Other blogs
参考资源
select机制提供了一个数据结构 struct fd_set,这个结构可以理解为一个集合,实际上是一个位图,每一个特定的位来标识相应的file descriptor,fd_set集合可以通过一些macro来操纵,macro的具体信息已经在上文给出
深入理解fd_set,便于论述,取fd_set长度为1个字节,fd_set中的每一个bit可以对应一个file descriptor,因此1个字节的fd_set最大可以对应8个file descriptors
fd_set set;
FD_ZERO(&set);
// 此时,set的内容是00000000,左边是高位,右边是低位
FD_SET(fd, &set);
// 如果fd=5, 那么执行上述语句之后,set的内容是00100000
FD_SET(2, &set);
FD_SET(1, &set);
// set 的内容是00100110
select(6, &set, NULL, NULL, 0);
// 执行select,阻塞等待
// 如果fd=1,fd=2上都发生可读事件,则set内容变为00000110,没有发生可读事件的fd=5在set上的对应bit被清零
select() 函数的格式如下:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
上文中叙述过每个参数的含义,这里再详细说一下
nfds是一个整数值,其值为所有的file descriptors中的最大值加1,因为,Linux系统中的file descriptor是从0开始的,0号文件是stdin,即标准输入流
从nfds
的拼写中也能看出,number of file descriptors
,即select()所monitor的file descriptors的数量。
对应到fd_set这个位图中,即是位图中有效bit的范围。
一个疑问
既然readfds,writefds,exceptfds都标识出了要监控的file descriptors都是谁,那为什么还要nfds这个参数呢?select() 是从0开始,一直监控到nfds-1吗?具体实现不清楚,但是,根据Linux Programmer’s Mannual中对Return Value的描述(见上文),select()只有在所监控的file descriptors变得ready之后、或者超时之后,才会return。这样看来,nfds参数的作用,可能是便于select()的实现吧!