select(poll)在应用程序中如何使用

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取不同的值,该调用就表现不同的性质:

1timeout0,调用立即返回;

2timeoutNULLselect()调用就阻塞,直到知道有文件描述符就绪;(当有文件描述符就绪时,会向这个函数发送信号,以唤醒此函数。)

3timeout为正整数,就是一般的定时器。

 

select的返回值有如下情况:

1.正常情况下返回就绪的文件描述符个数;

2.经过了timeout时长后仍无设备准备好,返回值为0

3.如果select被某个信号中断,它将返回-1并设置errnoEINTR

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定义的readfdswritefdserrorfds操作集进行操作最好使用封装的通用宏来处理:

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

上一篇:参数匿名函数字典排序、生成式和生成器、装饰器的作用


下一篇:java 8 Stream中操作类型和peek的使用