分类: LINUX
2015-02-10 10:05:32
原理:
select函数会等待,直到描述符句柄中有可用资源(可读、可写、异常)时返回,返回值是可用资源(可读/可写/异常等)描述符的个数(>0),0代表超时,-1代表错误。具体到内核大致是:当应用程序调用select() 函数, 内核就会相应调用 poll_wait(), 把当前进程添加到相应设备的等待队列上,然后将该应用程序进程设置为睡眠状态。直到该设备上的数据可以获取,然后调用wake_up()唤醒该应用程序进程。select每次轮训都会遍历所有描述符句柄。
函数接口:
int select(int max_fd,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
max_fd :fd+1
readfds :可读描述符句柄
writefds :可写描述符句柄
exceptfds:异常描述符句柄
timeout :超时时间
select函数的参数将告诉内核:
(1)我们所关心的对应描述符句柄
(2)对于每个描述符我们所关心的条件,是否可读,是否可写或是否异常
(3)希望等待多长时间,struct timeval * timeout
struct timeval{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
}
timeout == NULL 等待无限长的时间;
timeout->tv_sec == 0 &&timeout->tv_usec == 0不等待,直接返回;
timeout->tv_sec !=0 ||timeout->tv_usec!= 0 等待指定的时间,超时后返回0,表示在一定时间内没有可用资源的描述符。
一个描述符句柄保存在fd_set类型中,fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组。
fd_set类型变量是一下几个宏来控制它:
#include
int FD_ZERO(int fd, fd_set *fdset); --将一个fd_set类型变量所有位设置为0
int FD_CLR(int fd, fd_set *fdset); --将fd_set类型变量中对应fd删除
int FD_SET(int fd, fd_set *fd_set); --将fd添加到fd_set类型变量中,进而将fd_set类型变量的对应的位置位
int FD_ISSET(int fd, fd_set *fdset); --fd是否在fd_set类型变量中,对应为1说明资源可用
select函数执行结果:执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。错误
值可能为:
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数n 为负值。
ENOMEM 核心内存不足
从http://blog.csdn.net/lingfengtengfei/article/details/12392449中摘取“理解select模型:”
理解select模型:
理解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位置为1)
(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模型的特点:
(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
(3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
下面具体举个简单例子。
功能实现:
服务器回复从客户端接收到的数据-->回显数据
点击(此处)折叠或打开
注意:在使用select函数时,请注意一下几点
1,select第一个参数是最大描述加1;
2,描述集中在每次select后都要重新赋值,select函数会把不符合条件的对应位清空;
3,如果有时间参数,在超时后,时间参数都必须重新赋值,否则会一直超时。
参考博客:
http://blog.chinaunix.net/uid-21275705-id-224351.html
http://blog.csdn.net/lingfengtengfei/article/details/12392449
lidongwei262015-03-02 22:20:04
应将int FD_ZERO(int fd, fd_set *fdset); 修改为int FD_ZERO(fd_set *fdset);