I/O多路转接之poll
I/O多路转接之select链接: link.
解决了select的两个问题
- poll能等待的文件描述符是没有上限的
- poll不需要在每次调用的时候,都进行重新设置
1. Poll函数接口
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
pollfd结构
struct pollfd {
int fd; /* file descriptor /
short events; / requested events /(用户~内核)
short revents; / returned events */(内核~用户)(这个理论上我们不需要设置,他会给我返回)(short events 和 short revents这两个参数也就做到了将select中的输入输出型参数进行分隔开,也就不需要每次都重新进行设置)
};
参数说明
fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.
nfds表示fds数组的长度.
timeout表示poll函数的超时时间, 单位是毫秒(ms),如果是0表示没有timeout时间,为非阻塞状态,如果为-1表示永久阻塞状态
events和revents的取值:
对于这里的事件其实都是宏值,其中POLLIN对应0x001,POLLOUT对应0x004等
-
POLLIN:是否可以从操作系统中读数据
-
POLLOUT:是否可以向操作系统写数据(写的本质是操作系统的缓冲区有地方可以让你写,如果没有,那么写事件也是不会就绪的),这样理解会好很多
返回结果: -
返回值小于0, 表示出错;
-
返回值等于0, 表示poll函数等待超时;
-
返回值大于0, 表示poll由于监听的文件描述符就绪而返回.
2. 使用poll监控标准输入
#include<iostream>
#include<poll.h>
#include<unistd.h>
using namespace std;
int main()
{
struct pollfd rfds; //这里本来是一个结构体数组,但是这个代码只是一个监控标准输入的,所以并没有写成数组的形式
rfds.fd = 0;
rfds.events = POLLIN;
//rfds.revents = 0 这个参数是系统自己返回的,我们不需要写
char buf[1024] = {0};
cout << "poll begin"<< endl;
while(true){
switch(poll(&rfds,1,1000)){
case 0:
cout << "time out!"<< endl;
break;
case -1:
cerr << "poll error"<< endl;
break;
default:
cout << "events happen!"<< endl;
if(rfds.fd == 0 && (rfds.revents & POLLIN)){
//此时说明0号文件描述符的读事件已经就绪了
ssize_t s = read(0,buf,sizeof(buf));
buf[s] = 0;
cout << "echo# "<< buf << endl;
}
}
}
}
3. poll的优缺点
一段关于写poll的伪代码
poll的优点
不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.
- pollfd结构包含了要监视的event和发生的event,不再使用select“参数值”传递的方式. 接口使用比select更方便.
- poll并没有最大数量限制 (但是数量过大后性能也是会下降).
poll的缺点
poll中监听的文件描述符数目增多时
- 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
- 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.
- 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.