今天和小伙伴一起看select源码,感觉收获还是挺大的,网上这方面的资料还是挺少的,很少有分析select的具体实现是怎样实现的,所以,我把它整理一下,希望可以帮到想看看select具体是怎样实现的小伙伴,ok,不说废话了.
函数原型: int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval * timeout);
void FD_CLR(int fd,fd_set *set);
int FD_ISSET(int fd,fd_set *set);
void FD_SET(int fd,fd_set *set);
void FD_ZERO(fd_set *set);
这些函数的具体是干什么的我就不一一介绍了,对这些函数的介绍网上的资料挺多的,不了解的可以先了解一下.
首先看几个下面要提到的比较重要的结构体(ps:当然结构体很多,咱们只研究和它具体实现有关的);
typedef struct
{
unsigned long *in,*out,*ex;
unsigned long *res_in,*res_out,*res_ex;
}
- 首先,申请一个数组去保存文件描述符,但是,我去申请多大的数组去保存呢?大家都知道,一个进程允许打开的最大的文件描述符默认为1024,虽然说,现在可以改变一个进程打开文件描述符的值,但是,select的实现里定义的宏.
#define FD_SETSIZE 1024 //select能监控的最大的文件描述符的fd的值,这里并不是值,因为,如果,你调用select的进程再调用select之前已经占用了几个文件描述,那么,你的select监控的文件描述符将小于1024.
-
- 既然select要监控的文件描述符最大值为1024,那么,它到底是怎样去监控文件描述符上有可读,可写或异常事件发生呢?首先,我们预申请一个数组一个位图数组(位图法就是bitmap的缩写,所谓的bitmap就是用每一位来存放某种状态,适合于大规模的数据,但数据状态又不是很多的情况,通常是用来判断某个数据不存在);
- 理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
- (1)执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000
- (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位
- (3)若再加入fd=2,fd=1,则set变为0001,0011
- (4)执行select(6,&set,0,0,0)阻塞等待
- (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空.这也就是为什么我们在使用select之前必须把fd存储在一个数组中,当每次调用select时,需要向select中添加相应的文件描述符的监听事件.