select()和poll()系统调用的本质一样,前者在BSD UNIX中引入的,后者在System V中引入的。
一、select
应用程序中最广泛用到的是BSD UNIX中引入的select()系统调用,其原型如下:
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
select的第一个参数maxfdp是文件描述符集中要被检测的数目,这个值必须至少比待检测的最大文件描述符大1;
参数readfds指定了需要被读监测的文件描述符集;参数writefds指定了需要被写监测的文件描述符集;而参数errorfds指定了可能出现异常情况的文件描述符集。
timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。
struct timeval数据结构的定义如下:
struct timeval
{
int tv_sec; //秒单位
int tv_usec; //微秒单位
};
timeout取不同的值,该调用就表现不同的性质:
1.timeout为0,调用立即返回;
2.timeout为NULL,select()调用就阻塞,直到知道有文件描述符就绪;(当有文件描述符就绪时,会向这个函数发送信号,以唤醒此函数。)
3.timeout为正整数,就是一般的定时器。
select的返回值有如下情况:
1.正常情况下返回就绪的文件描述符个数;
2.经过了timeout时长后仍无设备准备好,返回值为0;
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno。
select()函数的接口主要是建立在一种叫fd_set结构体的基础上。这个结构体是一组文件描述符(fd)的集合。因为fd_set类型的长度在不同平台上是不同的,此应该用一组标准的宏定义来处理这个类变量。
我们来了解fd_set这个结构的定义:
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
#define __NFDBITS (8 * sizeof(unsigned long)) //32
#define __FD_SETSIZE 1024// 每个进程能打开的文件描述符的上限,可以更改
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)// 32
typedef __kernel_fd_set fd_set;
对用fd_set定义的readfds、writefds、errorfds操作集进行操作最好使用封装的通用宏来处理:
FD_ZERO(fd_set *set);//将文件描述符集fd_set中的值置0,如此以来对应所有位都被设置为0;
FD_SET(int fd,fd_set *set);//将一个文件描述符加入文件描述集中;
FD_CLR(int fd,fd_set *set)//将一个文件描述符从文件描述符集中清除;
FD_ISSET(int fd,fd_set *set)//判断文件描述符是否被置位。
下面是一个典型的程序片段:
FD_ZERO(&readset);
FD_SET(fd,&readset);
select(fd+1,&readset,NULL,NULL,NULL);
if(FD_ISSET(fd,readset){……}
需要注意的是每次调用select之前都需要重新设定fd_set集合。
二、poll
函数原型
#include
int poll(struct pollfd * fdarray, unsigned long nfds, int timeout);
返回值说明: >0准备好描述字的个数;
=0超时;
= -1表示出错。
第一个参数是一个pollfd结构体数组,其中包括了你想测试的文件描述符和事件, 事件由结构中事件域events来确定,调用后实际发生的时间将被填写在结构体的revents域。
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
};
等待事件的掩码:
POLLIN 普通或优先级带数据可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通或优先级带数据可写
POLLWRNORM普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR发生错误
POLLHUP发生挂起
POLLVAL 描述字不是一个打开的文件
第一部分为处理输入的四个常值,第二部分是处理输出的三个常值,第三部分是处理错误的三个常值。
poll处理三个级别的数据,普通normal,优先级带priority band,高优先级high priority,这些都是出于流的实现。
第二个参数nfds用来指定第一个参数数组元素个数。
第三个参数指定poll函数在返回前等待多长时间,单位为毫秒。当等待时间为0时,poll()函数立即返回,为-1则使poll()一直阻塞直到一个指定事件发生。
如果没有事件发生,revents会被清空,所以你不必多此一举。
例子如下:
int poll_two_normal(int fd1,int fd2)
{
struct pollfd poll_list[2];
int retval;
poll_list[0].fd = fd1;
poll_list[1].fd = fd2;
poll_list[0].events = POLLIN|POLLPRI;
poll_list[1].events = POLLIN|POLLPRI;
while(1)
{
retval = poll(poll_list,(unsigned long)2,-1);
/* retval 总是大于0或为-1,因为我们在阻塞中工作 */
if(retval
{
fprintf(stderr,"poll错误: %s/n",strerror(errno));
return -1;
}
if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
((poll_list[0].revents&POLLERR) == POLLERR) ||
((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
((poll_list[1].revents&POLLHUP) == POLLHUP) ||
((poll_list[1].revents&POLLERR) == POLLERR) ||
((poll_list[1].revents&POLLNVAL) == POLLNVAL))
return 0;
if((poll_list[0].revents&POLLIN) == POLLIN)
handle(poll_list[0].fd,NORMAL_DATA);
if((poll_list[0].revents&POLLPRI) == POLLPRI)
handle(poll_list[0].fd,HIPRI_DATA);
if((poll_list[1].revents&POLLIN) == POLLIN)
handle(poll_list[1].fd,NORMAL_DATA);
if((poll_list[1].revents&POLLPRI) == POLLPRI)
handle(poll_list[1].fd,HIPRI_DATA);
}
}
参考文档:
http://apps.hi.baidu.com/share/detail/5467807
http://hi.baidu.com/operationsystem/blog/item/208eab9821da8f0e6f068cea.html
http://hi.baidu.com/%CB%BC%BF%BCde%C2%AB%CE%AD/blog/item/7c5bd12755c0a24aad34ded8.html