poll()的实现与详解
poll不同于select使用三个位图表示fdset的方式,poll使用一个pollfd的指针来实现。因为select的实现和poll的实现非常类似,知识描述fd集合的方式有所不同,poll使用的是pollfd而不是select中的fd_set结构,其他类似,所以poll的实现相比于select实现简单。首先我们看下Linux下的poll()是如何描述的一、poll()函数
poll不同于select使用三个位图表示fdset的方式,poll使用一个pollfd的指针来实现。
因为select的实现和poll的实现非常类似,知识描述fd集合的方式有所不同,poll使用的是pollfd而不是select中的fd_set结构,其他类似,所以poll的实现相比于select实现简单。
首先我们看下Linux下的poll()是如何描述的
一、poll()函数
头文件
#include <poll.h>
函数原型
int poll(struct pollfd *fds, nfds_t nfds,int timeout);
参数说明
fds
存放需要被检测状态的套接字描述符,与select不同(select在调用之后会清空这个数组),每当调用这个数组,系统不会清空这个数组,而是存放revents状态变化描述符变量,这样才做起来很方便。
nfds
用于标记数组fd中struct pollfd结构元素的总数量
timeout
poll函数调用阻塞时间,单位是毫秒(ms)
值
INFTIM 永远等待
0 立即返回,不阻塞进程
>0 等待指定数目的毫秒数
二、struct pollfd结构
poll函数的第一个参数是一个数组,里面存放的是struct pollfd结构,struct pollfd结构如下
struct pollfd {int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
fd 文件描述符
events 请求的事件
revents 返回的事件
events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。
poll函数通过在revents中设置标志POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。
如果fd小于0, 则events字段被忽略,而revents被置为0.
标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRL 高优先级带数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
poll函数的事件标志符值
注意:
1)后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。
2)第二个参数nfds:要监视的描述符的数目。
3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。
相比于select机制,poll机制采用链表来进行文件描述符的存储,因此它并没有最大连接数的限制。
但同样存在一些缺点:
1、大量的 fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。2、 poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
三、poll实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
int main(int argc, char* argv[])
{
struct pollfd fds[2];//文件描述符
fds[0].fd = 0;
fds[0].events = POLLIN;//对读事件感兴趣
fds[0].revents = 0;//输出型
fds[1].fd = 1;
fds[1].events = POLLOUT;//对写事件感兴趣
fds[1].revents = 0;
char buf[1024];
int done = 0;
int i = 0;
int timeout = 1000;//1000ms 1秒
while (!done)
{
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout);
switch (ret)
{
case -1:
perror("poll");
exit(2);
break;
case 0:
printf("timeout...");
default:
//有事件就绪,要判断哪个文件描述符的事件所以要遍历fds
for (i = 0; i < sizeof(fds) / sizeof(fds[0]); ++i)
{
//是否可读 fds[i]关心的是读,读事件发生
if (fds[i].fd == 0 && (fds[i].revents&POLLIN))
{
memset(buf, '\0', sizeof(buf));
ssize_t _s = read(0, buf, sizeof(buf) - 1);
if (_s > 0)
{
buf[_s - 1] = '\0';
if (strncmp(buf, "quit", 4) == 0)
{
close(fds[i].fd);
exit(0);
}
printf("echo#.%s\n", buf);
}
}
if (fds[i].fd == 1 && (fds[1].revents&POLLOUT))
{
memset(buf, '\0', sizeof(buf));
strcpy(buf, "Hello!");
printf("echo#.%s\n", buf);
sleep(3);
}
}
break;
}
}
return 0;
}
实现效果
更多推荐
所有评论(0)