select介绍
在linux中, 主要的 IO复用方式中, 有epoll, poll 和select, 这次先来学习下select.
select 能够同时监视多个文件描述符的变法, 也支持超时返回.
先来看下select函数的定义
/*/usr/include/sys/select.h*/ externintselect(int__nfds,//最大文件描述符+1 fd_set*__restrict__readfds,//读状态文件集 fd_set*__restrict__writefds,//写状态文件集 fd_set*__restrict__exceptfds,//异常状态文件集 structtimeval*__restrict__timeout);//超时时间
如上图函数声明所示, 不管我们关注什么状态, 我们都应该把同一类状态的文件描述符存到同一个fd_set集合,以便select能够相应的位置打上标签, 以便后续我们来判断该文件描述符是否已经准备好
这些传递给select函数的参数, 将告诉内核:
- 我们需要监听的文件描述符
- 对于每个文件描述符, 我们所关心的状态 (读/写/异常)
- 我们要等待多长时间 (无限长/超时返回)
而内核也会通过select的返回, 告知我们一些信息:
- 已经准备好的文件描述符个数
- 那三种状态分别是哪些文件描述符
我们可以通过以下方式将关注的文件描述符加入相应的文件集:
intsocket_test; socket_test=socket(...);//创建socket文件描述符 connent(socket_test,..);//连接服务端 FD_SET(socket_test,&rdfds);//加入读状态文件集 FD_SET(socket_test,&wdfds);//加入写状态文件集 ....
select原理
select函数执行顺序是: SYSCALL_DEFINE5 (sys_select) -> core_sys_select -> do_select
我们都知道, select 支持监听三个文件集: 读文件集, 写文件集, 异常文件集;
在我们调用FD_SET(socket_test, &rdfds)时, 实际上执行的操作是: 在rdfds成员数组中, 将__FDELT (d)位置的值 设成 __FDMASK (d), 直接说会有点疑惑, 先看下相关的函数,宏定义是怎样定义的吧:
/*取自:/usr/include/sys/select.h*/ #defineFD_SET(fd,fdsetp)__FD_SET(fd,fdsetp) typedeflongint__fd_mask; /*取自:/usr/include/bits/select.h*/ #define__NFDBITS(8*(int)sizeof(__fd_mask)) #define__FDELT(d)((d)/__NFDBITS) #define__FDMASK(d)((__fd_mask)1<<((d)%__NFDBITS)) #define__FD_SET(d,set)(__FDS_BITS(set)[__FDELT(d)]|=__FDMASK(d)) typedefstruct { /*XPG4.2requiresthismembername.Otherwiseavoidthename fromtheglobalnamespace.*/ #ifdef__USE_XOPEN __fd_maskfds_bits[__FD_SETSIZE/__NFDBITS]; #define__FDS_BITS(set)((set)->fds_bits) #else __fd_mask__fds_bits[__FD_SETSIZE/__NFDBITS]; #define__FDS_BITS(set)((set)->__fds_bits) #endif }fd_set; /*/usr/include/linux/posix_types.h*/ #define__FD_SETSIZE1024
举个栗子, 假设 fd=3, 当我们执行FD_SET(fd, &rdfds)时:
- 算出 __FDELT(d) 和 __FDMASK(d)的值,
转载请注明:IT运维空间 » 运维技术 » 在Linux环境下select函数的初体验
发表评论