简介
poll/ppoll其实是同select/pselect类似的,用于同步多路I/O复用。poll/ppoll同select/pselect的最大区别在于poll/ppoll没有最大连接数限制,因为poll/ppoll使用的用于存储fd的集合是根据实际fd的数量动态分配的。此外,poll/ppoll监听的事件类型更加的丰富/细致些。
相关结构体定义
struct pollfd
/* Data structure describing a polling request. */
struct pollfd {
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
结构成员说明
fd: 将要通过poll监听的fd;
events: 输入参数,通过poll监听fd的事件;
revents: 输出参数,poll监听获取到fd上实际发生的事件。
事件类型说明
POLLIN: IN/OUT。在events中设置,表示在fd上监听读事件;在revents中设置,表示fd上发生了读事件。
POLLPRI: IN/OUT。在events中设置,表示在fd上监听紧急数据读事件;在revents中设置,表示fd上发生了紧急数据读事件。紧急数据读事件包括:
- TCP的带外数据;
- 包模式下的伪终端主机在从机中出现了状态变化。
POLLOUT: IN/OUT。在events中设置,表示在fd上监听写事件;在revents中设置,表示fd上发生了写事件。
POLLRDHUP: IN/OUT。在events中设置,表示在fd上监听读挂起事件;在revents中设置,表示fd上发生了读挂起事件。读挂起事件是指在如TCP这样的流式socket上发生的连接对端已经关闭了连接,而本段还没关闭,使得该连接处于半连接状态。处于该状态下的fd是不允许写操作,而允许读操作。该事件是从linux内核版本2.6.17起才支持的。
POLLERR: OUT。只会出现在revents中,表示fd上发生了错误。
POLLHUP: OUT。连接对端处于挂起状态。
POLLNVAL: OUT。无效的请求数据,通常是由于fd是无效的造成。
POLLRDNORM: IN/OUT。需在编译时定义了宏 _XOPEN_SOURCE 时才可以用。作用同POLLIN相同。
POLLRDBAND: IN/OUT。需在编译时定义了宏 _XOPEN_SOURCE 时才可以用。在events中设置,表示在fd上监听具有优先级频带数据的读事件;在revents中设置,表示fd上发生了具有优先级频带数据的读事件。该事件通常Linux上是无用的。
POLLWRNORM: IN/OUT。需在编译时定义了宏 _XOPEN_SOURCE 时才可以用。作用同POLLOUT相同。
POLLWRBAND: IN/OUT。需在编译时定义了宏 _XOPEN_SOURCE 时才可以用。在events中设置,表示在fd上监听优先数据的可写入事件;在revents中设置,表示fd上发生了允许优先数据事件。
struct timespec
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
相关函数介绍
poll
依赖头文件
#include <poll.h>
函数定义
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能和select相同,同时监听多个fd上的指定事件。直到一个或多个fd上有一个或多个事件发生时,或超时时才返回。
参数说明
fds: 指向struct pollfd结构数组的指针。数组的每个struct pollfd结构体对象标识一个fd及其相关事件。
nfds: 表示fds数组中的数组成员个数,即一次poll操作监听的fd的数量。
timeout: 超时时间,单位毫秒。
返回值说明
成功返回大于等于0,返回大于0表示三个fd集合中触发事件的fd的总个数,等于0表示等待超时;错误返回-1,错误码从errno中获取。
错误码说明
EFAULT: fds的内存地址错误,该地址不包含系统分配给进程的内存地址范围内。
EINTR: 在事件监听过程中被信号中断了。
EINVAL: nfds的数量超过了系统限制(RLIMIT_NOTFILE)。
ENOMEM: 内存不足错误,导致没有内存用于poll内部使用。
ppoll
依赖头文件
#define _GNU_SOURCE
#include <poll.h>
要使用ppoll,需地定义宏_GNU_SOURCE。
函数定义
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *timeout_ts,
const sigset_t *sigmask);
ppoll功能同poll相似。
参数说明
fds: 指向struct pollfd结构数组的指针。数组的每个struct pollfd结构体对象标识一个fd及其相关事件。
nfds: 表示fds数组中的数组成员个数,即一次poll操作监听的fd的数量。
timeout_ts: 超时时间,struct timespec结构形式,精度到纳秒,实际在底层实现时精度只到了毫秒。
sigmask: 指向一个设置了信号掩码信息的结构。如果不为NULL,则ppoll先将当前的信号掩码替换成sigmask指向的信号掩码,再执行poll,最后再替换会原来的信号掩码。
返回值说明
成功返回大于等于0,返回大于0表示三个fd集合中触发事件的fd的总个数,等于0表示等待超时;错误返回-1,错误码从errno中获取。
错误码说明
EFAULT: fds的内存地址错误,该地址不包含系统分配给进程的内存地址范围内。
EINTR: 在事件监听过程中被信号中断了。
EINVAL: nfds的数量超过了系统限制(RLIMIT_NOTFILE)。
ENOMEM: 内存不足错误,导致没有内存用于poll内部使用。
poll和ppoll比较
- poll的timeout使用的是int类型,单位为毫秒,所以精度为毫秒;ppoll的timeout使用的是struct timespec,精度为纳秒。
- ppoll带有sigmask参数,poll不带sigmask参数。
用例展示
poll
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <poll.h>
int
main(void)
{
struct pollfd *fds;
int retval;
fds = (struct pollfd *)calloc(2, sizeof(struct pollfd));
//stdin
fds[0].fd = stdin;
fds[0].events = POLLIN;
fds[0].revents = 0;
//stdout
fds[1].fd = stdout;
fds[1].events = POLLOUT;
fds[1].revents = 0;
retval = poll(rfds, 2, 5);
if (retval == -1)
perror("poll()");
else if (retval) {
if (fds[0].revents & POLLIN) {
printf("Data is available now with stdin.\n");
}
if (fds[1].revents & POLLOUT) {
printf("Data can write to stdout.\n");
}
} else
printf("No data within five seconds.\n");
free(fds);
exit(EXIT_SUCCESS);
}
ppoll
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <poll.h>
int
main(void)
{
struct pollfd *fds;
int retval;
struct timespec ts;
fds = (struct pollfd *)calloc(2, sizeof(struct pollfd));
//stdin
fds[0].fd = stdin;
fds[0].events = POLLIN;
fds[0].revents = 0;
//stdout
fds[1].fd = stdout;
fds[1].events = POLLOUT;
fds[1].revents = 0;
//timeout
ts.tv_sec = 5;
ts.tv_nsec = 0;
retval = ppoll(rfds, 2, &ts, NULL);
if (retval == -1)
perror("ppoll()");
else if (retval) {
if (fds[0].revents & POLLIN) {
printf("Data is available now with stdin.\n");
}
if (fds[1].revents & POLLOUT) {
printf("Data can write to stdout.\n");
}
} else
printf("No data within five seconds.\n");
free(fds);
exit(EXIT_SUCCESS);
}