多路复用IO–poll
poll定义
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
返回值:准备就绪的描述符数目:若超时,返回0;若出错,返回-1
与select不同,poll不是为每个条件(可续性、可写性和异常条件)构造一个描述符集,而是构造一个pollfd结构的数组,每个数组元素指定一个描述符编号以及我们对该描述符感兴趣的条件。
struct pollfd {
int fd; /* file descriptor to check, or < 0 to ignore */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
}
参数说明
(1)fdarray数组中的元素数由nfds指定。
(2)应将每个数组元素的events成员设置为下图中所示值的一个或几个,通过这些值告诉内核我们关心的是每个描述符的哪些事件。返回时,revents成员由内核设置,用于说明每个描述符发生了哪些事件。
标志名 | 输入至events? | 从revents得到结果? | 说明 |
---|---|---|---|
POLLIN | y | y | 可以不阻塞地读高优先级数据意外的数据(等效于POLLRDNORM|POLLRDBAND) |
POLLRDNORM | y | y | 可以不阻塞地读普通数据 |
POLLRDBAND | y | y | 可以不阻塞地读优先级数据 |
POLLPRI | y | y | 可以不阻塞地读高优先级数据 |
POLLOUT | y | y | 可以不阻塞地写普通数据 |
POLLWRNORM | y | y | 与POLLOUT相同 |
POLLWRBAND | y | y | 可以不阻塞地写优先级数据 |
POLLERR | y | 已出错 | |
POLLHUP | y | 已挂断 | |
POLLNVAL | y | 描述符没有引用一个打开文件 |
(3)poll的最后一个参数指定的是我们愿意等待多长时间。
timeout == -1
永远等待。
timeout == 0
不等待。测试所有描述符并立即返回。
timeout > 0
等待timeout毫秒。当指定的描述符之一已准备好,或timeout到期时立即返回。如果timeout到期时还没有一个描述符准备好,则返回值是0。
实例讲解
下面已一个例子来演示poll的用法。
我们有两个程序,poll.c和write_fifo.c。
poll.c中,我们循环监听了两个描述符,STDIN_FILENO标准输入描述符和命名管道fd。不同的fd上有数据反馈,在显示器上会打印不同的输出。
write_fifo.c中,每隔5秒向命名管道fd写入”this is for test”。
/**
* poll.c
* 演示poll的主要用法
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <poll.h>
#define fifo_filename "test_fifo"
int main(int argc, char *argv[])
{
int ret;
int fd;
struct pollfd fds[2];
ret = mkfifo(fifo_filename, 0666);
if (ret != 0) {
perror("mkfifo error");
}
fd = open(fifo_filename, O_RDWR);
if (fd < 0) {
perror("open error");
exit(-1);
}
ret = 0;
fds[0].fd = 0;
fds[1].fd = fd;
fds[0].events = POLLIN;
fds[1].events = POLLIN;
while (1) {
ret = poll(fds, 2, -1);
if (ret == -1) {
perror("epoll_wait error");
} else if (ret > 0) {
char buf[100] = {0};
if ((fds[0].revents & POLLIN) == POLLIN) {
read(0, buf, sizeof(buf));
printf("stdin buf = %s\n", buf);
} else if ((fds[1].revents & POLLIN == POLLIN)) {
read(fd, buf, sizeof(buf));
printf("fifo buf = %s\n", buf);
}
} else if (ret == 0) {
printf("time out\n");
}
}
exit(0);
}
/**
* write_fifo.c
* 给命名管道发送信息
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#define fifo_filename "test_fifo"
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
ret = mkfifo(fifo_filename, 0666);
if (ret != 0) {
perror("mkfifo error");
}
fd = open(fifo_filename, O_RDWR);
if (fd < 0) {
perror("open error");
exit(-1);
}
while (1) {
char *str = "this is for test";
write(fd, str, strlen(str));
printf("after write to fifo\n");
sleep(5);
}
exit(0);
}
编译两个程序
/myblog/myblog/source/poll# gcc poll.c -o poll
/myblog/source/poll# gcc write_fifo.c -o write_fifo
启动poll程序,如下:
/myblog/source/poll# ./poll
fifo buf = this is for test
fifo buf = this is for test
fifo buf = this is for test
hello
stdin buf = hello
fifo buf = this is for test
fifo buf = this is for test
^C
启动write_fifo程序,如下:
/myblog/source/poll# ./write_fifo
mkfifo error: File exists
after write to fifo
after write to fifo
after write to fifo
after write to fifo
after write to fifo
^C
从程序运行结果可以看出,当write_fifo不断往命名管道写入”this is for test”时,select监听到命名管道fd已准备好,从fd上读出数据并打印到显示器上;当我们从标准输入中打出hello时,select监听到STDIN_FILENO(描述符为0)已准备好,从描述符0中读出数据并打印到显示器上。