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);
1 参数 fds --- 是一个struct pollfd[ ] 数组,用来表示文件描述符的监视信息。
2 参数nfds --- 给出了需要监视的描述符的数目。
3 参数 timeout --- 是一个用毫秒表示的时间,是poll在返回前没有接收事件时应该等待的时间。如果timeout的值为 -1,poll就永远都不会超时。如果整数值为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。
我们将这些标记分为三个部分:
1 处理输入的4个常值:POLLIN, POLLRDNORM, POLLRDBAND, POLLPRI
2 处理输出的3个常值:POLLOUT, POLLWRNORM, POLLWRBAND
3 处理错误的 3 个常值:POLLERR, POLLHUP, POLLNVAL
poll识别三个类别的数据:普通(normal),优先级带(priority band)和高优先级(high priority),这些术语均出自基于流的实现。
POLLIN可以被定义为POLLRDNORM和POLLRDBAND的逻辑或常值。POLLIN存在于SVR3的实现,它要早于SVR4中的优先级带,所以,此常值保持了向后兼容性。类似地,POLLOUT等效于POLLWRNORM,前者早于后者。
第一个参数 struct pollfd 结构的定义如下:
//该结构是应用层poll(); 函数监听文件描述符集合中用到。它存放了一个需要
//监听的文件描述符结构。
struct pollfd {
int fd; //需要监听的文件描述符
short events; //存放需要监听的事件
short revents; //存放产生的事件
};
fd是文件描述符值,events和revents是通过对代表各种事件的标识符进行逻辑或运算构建而成的,上面列举了这些标记,表示相应的事件。设置events来包含要监视的事件,poll用已经发生的事件来填写revents。
poll函数通过revents中设置标识符POLLHUP, POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对与这些标识符相关的比特位进行设置。
如果fd小于零,那么events字段被忽略。所以,如果我们要忽略该结构,就设置fd = -1,那么,events字段被忽略,而revents在调用poll(); 函数返回之后,就被设置为0。
标准中没有说明应该如何处理文件结束,文件结束可以通过revents的标识符POLLHUP或者返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,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(); 函数屏蔽信号处理。
- poll()
- poll
- poll
- poll
- poll()
- poll
- poll
- POLL
- poll
- poll
- poll
- poll
- poll
- poll
- poll
- poll
- Poll Test
- poll分析
- Xss、Sql、Emoji过滤器+SpringMVC JSON过滤
- 第一章 计算机性能评价
- MySQL 日期类型及默认设置
- 哎呀呀
- ThinkPHP5使用缓存
- poll
- leetcode-78. Subsets
- 新路程------imx6 sd读写测试
- 什么是AI?它的应用领域是什么?
- Qt Creator的安装及创建C项目示例
- php 调试利器debug_print_backtrace()
- 使用云上资源的你遇到过哪些有趣的事情
- 360浏览器极速模式对iframe的支持
- RPC协议