poll

来源:互联网 发布:硬盘文件恢复软件 编辑:程序博客网 时间:2024/05/18 12:32

poll函数讲解

       当前对poll(); 函数的讲解参考《UNIX系统编程》P98 poll() 函数。

       poll(); 函数与select(); 函数类似,但是,它是用文件描述符而不是条件的类型来组织信息的。也就是说,一个文件描述符的可能事件都存储在 struct pollfd 结构中。与之相反,select 用事件的类型来组织信息,而且,读、写和出错的情况都有独立的描述符掩码。poll函数是POSIX::XSI扩展的一部分,它起源于UNIX System V

       poll(); 函数有三个按时,格式如下:

int poll(struct pollfd *fds, unsigned long nfds, int timeout);

参数 fds --- 是一个struct pollfd[ ] 数组,用来表示文件描述符的监视信息。

参数nfds --- 给出了需要监视的描述符的数目。

参数 timeout --- 是一个用毫秒表示的时间,是poll在返回前没有接收事件时应该等待的时间。如果timeout的值为 -1poll就永远都不会超时。如果整数值为32个比特,那么,最大的超时周期大约为30分钟。有如下设置:

timeout的值

解释

INFTIM

永远等待

0

立即返回,不阻塞

> 0

等待指定数目的毫秒数

       如果超时,poll函数返回0,如果成功,poll返回拥有事件的描述符的数目。如果不能够,poll返回 -1 并设置errno。下表列出了设置events监测的信号,以及设置到revents返回的信号。

/* These are specified by iBCS2 */

#define POLLIN       0x0001     //普通或优先级带数据可读

#define POLLPRI      0x0002     //高优先级数据可读

#define POLLOUT      0x0004     //普通数据可写

#define POLLERR      0x0008     //发生错误

#define POLLHUP      0x0010     //发生挂起

#define POLLNVAL  0x0020     //描述字不是一个打开的文件

 

/* The rest seem to be more-or-less nonstandard. Check them! */

#define POLLRDNORM   0x0040     //普通数据可读

#define POLLRDBAND   0x0080     //优先级带数据可读

#define POLLWRNORM   0x0100     //普通数据可写

#define POLLWRBAND   0x0200     //优先级带数据可写

#define POLLMSG      0x0400     //比较少用,查man 说该标记少用

#define POLLREMOVE   0x1000     //查man 找不到该标记说明,但是在网上查到: 在Solaris 7系统上,sun 引入了/dev/poll 设备,该标记就是从/dev/poll 列表中删除对应的fd。

       我们将这些标记分为三个部分:

处理输入的4个常值:POLLIN, POLLRDNORM, POLLRDBAND, POLLPRI

处理输出的3个常值:POLLOUT, POLLWRNORM, POLLWRBAND

处理错误的 3 个常值:POLLERR, POLLHUP, POLLNVAL

       poll识别三个类别的数据:普通(normal),优先级带(priority band)和高优先级(high priority),这些术语均出自基于流的实现。

       POLLIN可以被定义为POLLRDNORMPOLLRDBAND的逻辑或常值。POLLIN存在于SVR3的实现,它要早于SVR4中的优先级带,所以,此常值保持了向后兼容性。类似地,POLLOUT等效于POLLWRNORM,前者早于后者。

第一个参数 struct pollfd 结构的定义如下:

//该结构是应用层poll(); 函数监听文件描述符集合中用到。它存放了一个需要

//监听的文件描述符结构。

struct pollfd {

    int fd;              //需要监听的文件描述符

    short events;     //存放需要监听的事件

    short revents;       //存放产生的事件

};

       fd是文件描述符值,eventsrevents是通过对代表各种事件的标识符进行逻辑或运算构建而成的,上面列举了这些标记,表示相应的事件。设置events来包含要监视的事件,poll用已经发生的事件来填写revents

       poll函数通过revents中设置标识符POLLHUP, POLLERRPOLLNVAL来反映相关条件的存在。不需要在events中对与这些标识符相关的比特位进行设置。

       如果fd小于零,那么events字段被忽略。所以,如果我们要忽略该结构,就设置fd = -1,那么,events字段被忽略,而revents在调用poll(); 函数返回之后,就被设置为0

       标准中没有说明应该如何处理文件结束,文件结束可以通过revents的标识符POLLHUP或者返回0字节的常规读操作来传达。即使POLLINPOLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。

注意:

       select(); 调用对传递给它的文件描述符集进行了修改,所以,每次在调用select(); 函数的时候,都必须对其参数进行重新设置。poll(); 函数为输入和返回值使用了相互独立的变量,因此,不必在每次调用poll(); 之后重置被监视的描述符列表。poll(); 函数有很多优点,不需要在每次调用后重新设置掩码。

       就是说,当调用poll(); 函数返回之后,如果有信号要处理,就设置了监听结构的revents 成员,那么,当我们再次调用poll(); 函数的时候,不需要给revents清零。因为,每次调用poll(); 函数的时候,内核都会认为revents输入的是0,当有信号产生的时候,才会设置revents成员,如果没有信号产生,就revents的值就是0

select(); 不同,poll(); 函数将错误当作引起poll(); 返回的事件来处理。尽管参数timeout的范围受限,但它更容易使用,此外,poll(); 函数不需要使用max_fd参数。

3.1.2 poll函数的应用

       在上面“1.5.2 客户端的例子”的基础上增加如下成员函数:

#define MAX_SCAN_FDSET   10  //定义poll 监听的文件描述符集合的大小

#define POLL_WAIT_TIMEOUT   10*1000    //单位是毫秒

//=================================================================

//处理poll(); 模式

//=================================================================

void client_sock::work_poll()

{

    int i = 0, ret = 0;

    struct pollfd scan_fdset[MAX_SCAN_FDSET];

    int scan_fdset_num = 0;

    char recv_buf[BUF_LEN];

 

    bzero(recv_buf, BUF_LEN);

    bzero(scan_fdset, sizeof(struct pollfd)*MAX_SCAN_FDSET);

 

    scan_fdset[0].fd = fd;

    //注意,在《UNIX网络编程第一卷》P162中提到:POLLERR,POLLHUP,POLLNVAL 这些错误信号在events 中不能够设置,

    //当相应的条件触发的时候,它们会在revents 中返回。

    scan_fdset[0].events = POLLIN;  //设置需要监听的信号

 

    //数组中的其他元素设置为不可用

    for(i = 1; i < MAX_SCAN_FDSET; i++)

    {

       scan_fdset[i].fd = -1;

    }

 

    scan_fdset_num = 1;  //表示当前的scan_fdset[] 数组中只使用前面1 个元素存放需要监听的扫描符

 

    while(1)

    {

       ret = poll(scan_fdset, scan_fdset_num, POLL_WAIT_TIMEOUT);

       sleep(1);

       printf("scan_fdset[0].revents = %d \n", scan_fdset[0].revents);

       deal_select_ret(ret);    //处理返回值,与select 一样

 

       for(i = 0; i < MAX_SCAN_FDSET; i++)

       {

           if(scan_fdset[i].revents & POLLIN)

           {

              debug_printf("fd have signal!");

              ret = read(fd, recv_buf, BUF_LEN);

              if(-1 == ret)

              {

                  perror_printf("read err!");

                  continue;

              }

              show_data(recv_buf, BUF_LEN);

           }

       }//end of for

    }//end of while(1)

}

然后,在main(); 函数中调用该函数,如下:

int ret = 1;

client_sock client((char*)"127.0.0.1", 8861);

 

ret = client.init_client();

if(1 == ret)

{

    return 1;

}

client.work_poll();

运行的结果如下:

[weikaifeng@weikaifeng client]$ ./client

connect to server success! ip = 127.0.0.1, port = 8861

scan_fdset[0].revents = 1

client_sock.cpp(224)fd have signal!

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

scan_fdset[0].revents = 0

client_sock.cpp(218)select time out:: Success

scan_fdset[0].revents = 0

client_sock.cpp(218)select time out:: Success

3.1.3 poll函数被信号中断

       poll(); 函数如同select(); 函数一样,在阻塞等待的时候,能够被信号中断,具体的测试可以参考“2.1 select被信号中断”。

       所以,相应select(); 函数能够有 pselect(); 屏蔽信号,那么poll(); 函数也是由ppoll(); 函数屏蔽信号处理。