IO多路复用之poll
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*/
};
参数说明:
- fds是一个poll函数监听的结构列表。每个元素中,包含3部分的内容:文件描述符,监听的事件集合,返回的事件集合。
- nfds表示fds数组的长度。
- timeout表示poll函数的超时时间,单位是ms.
返回结果:
- 返回值<0,表示出错;
- 返回值=0,表示poll函数等待超时;
- 返回值>0,表示poll由于监听的文件描述符就绪而返回。
poll就绪条件
同select
读就绪
- socket 内核中,接收缓冲区中的字节数,大于等于低水位标记SO_RCVLOWAT(so_rcvlowat),此时可以无阻塞的读文件描述符,并且返回值大于0;
- socket TCP通信中,对端关闭连接,此时对该socket读,则返回0;
- 监听的socket上有新的连接请求;
socket上有未处理的错误。
缓冲区设置的原因:减少访问IO的次数,从而提高效率(因为访问IO的成本远远高于访问内存的)
写就绪
- socket 内核中,发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小),大于等于低水位SO_RCVLOEAT,此时可以无阻塞的写,并且返回值大于0;
- socket 的写操作被关闭(close或者shutdown)。对一个写操作被关闭的socket进行写操作,会触发SIGPIPE信号;
- socket 使用非阻塞connect连接或失败之后;
- socket 上有未读的错误。
异常就绪
- socket 上收到带外数据。关于带外数据,和TCP紧急模式相关(TCP协议头中,有一个紧急指针的字段)。
poll 优点
不同于select使用3个位图来表示3个fdset的方式,poll使用一个pollfd的指针来实现。
- pollfd结构包含了要监视的event和发生的event,将输入和输出分开了,所以接口使用更好用。
- poll并没有最大数量的限制。
poll 缺点
- 和select函数一样,poll返回后,需要轮询polled来获取就绪的文件描述符。
- 每次调用poll都需要把大量的polled结构从用户态拷贝到内核态。甚至远远高于select的拷贝,比select的效率更低。
- 和select函数一样,每次调用poll都需要再内核遍历传递进来的所有fd.
poll示例:使用poll来监控标准输入
用poll实现IO多路复用特点:
- 将所有的等待过程交给poll;
- 只要由文件描述符返回,就可以直接进行文件描述符的读写过程。
代码:
poll.c:
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <poll.h>
4
5 int main()
6 {
7 struct pollfd fds;
8 fds.fd = 0; // 表示标准输入
9 fds.events = POLLIN; // 表示要监控的是读状态
10 int ret = poll(&fds, 1, -1); // timeout返回值是-1,表示阻塞式等待
11 if(ret < 0)
12 {
13 perror("poll");
14 return 1;
15 }
16
17 char buf[1024] = {0};
18 ssize_t read_size = read(0, buf, sizeof(buf)-1);
19 if(read_size < 0)
20 {
21 perror("read");
22 return 1;
23 }
24 if(read_size == 0)
25 {
26 printf("read done!\n");
27 return 0;
28 }
29 buf[read_size] = '\n';
30
31 printf("%s\n", buf);
32 return 0;
33 }