Linux下IO多路复用(二)

poll

一、poll说明

  1. poll的机制与select类似,同样是轮询多个描述符,再根据描述符的状态进行处理;
  2. 但是poll没有描述符数量的限制,这个与机器的上限有关;
  3. 缺点与select类似即需要将大量的描述符从用户态复制至内核态;而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增加。

二、函数原型

1
int poll(struct pollfd fd[], nfds_t nfds, int timeout);

结构体struct pollfd

1
2
3
4
5
6
struct pollfd
{
int fd ; //文件描述符
short events; // 等待发生的 事件
short revents; // 实际发生的事件
}

事件 events

1
2
3
4
5
6
7
8
POLLIN  //有数据可读
POLLRDNORM //有普通数据可读
POLLRDBAND //有优先数据可读
POLLPRI //有紧急数据可读
POLLOUT //数据可写
POLLWRNORM //普通数据可写
POLLWRBAND //优先数据可写
POLLMSGSIGPOLL //消息可用

返回事件 revent

还包括

1
2
3
POLLERR  //指定描述符发生错误
POLLHUP //指定文件描述符挂起事件
POLLNVAL //指定描述符非法

nfds_t nfds

  1. 要监视的描述符的数目。

int timeout

  1. 是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。

  2. 如果值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

    1. INFTIM //永远等待
    2. 0 //立即返回,不阻塞进程
    3. 正值 //等待指定数目的毫秒数

返回值

  1. 成功时,poll()返回结构体中revents域不为0的文件描述符个数;
  2. 如果在超时前没有任何事件发生,poll()返回0;
  3. 失败时,poll()返回-1;
  1. 使用poll与select不一样,因为它们在合适的时候总是会从revents返回;
  2. POLLIN | POLLPRI 等价于select的读事件;
  3. POLLOUT|POLLWRBAND 等价于select的写事件;
  4. 而POLLOUT等价于POLLWRNORM。
  5. 注意这些表示并不是互斥的,它们可以被同时设置,表示这个文件描述符的读取和写入都会正常返回而不阻塞。
  6. timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。
  7. timeout指定为负数表示无限超时,使poll一直挂起直到一个指定事件的发生。

三、伪代码

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/// Handle poll input types
#define READ_ONLY ( POLLIN | POLLPRI )
/// Handle poll error types
#define POLL_ERROR ( POLLERR | POLLHUP | POLLNVAL )
/// Handle pool output types
#define POLL_OUTPUT ( POLLOUT )

socket...;
bind...;
listen...;
int i=0;
struct pollfd pollfds[MAXCN];
for(i=0;i<MAXCN; i++)
pollfds[i].fd = -1; //初始化poll结构中的文件描述符fd

pollfds[0].fd = serverfd; //tcp描述符
pollfds[0].events = READ_ONLY; // 普通或优先级带数据可读POLLIN

setNonBlocking(serverfd); //配置非阻塞模式

int timeout = 30000; /* 设定3秒后超时 */
int maxfd=0;
while(1)
{
//对加入poll结构体数组所有元素进行监测
int ret = poll(pollfds, static_cast<unsigned long>(maxfd+1),timeout);
if(ret < 0) //没有找到有效的连接 失败
{
//error
break;
}
else if(ret == 0)
{
//timeout
continue;
}
//监测serverfd(监听套接字)是否存在连接
if(pollfds[0].revents & POLLIN)
{
//从tcp完成连接中提取客户端
int clientfd = accept...
//添加新的fd 到数组中 判断有效的连接数是否小于最大的连接数,如果小于的话,就把新的连接套接字加入集合
if(maxfd >= MAXCN)
{
//超出链接数量
//关闭
}
else
{
//加入poll监视
setNonBlocking(clientfd); //配置非阻塞模式
}
}
//继续响应就绪的描述符
for()
{
if(pollfds[i].revents & (READ_ONLY | POLL_ERROR))
callReadBackFunC();
}
}

四、poll完整代码示例