POLL笔记

函数原型

1
2
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:

  • fds:一个pollfd结构体数组指针,用于描述需要等待的事件,每个数组中包含一个文件描述符

  • nfds:fd 数组的大小,即需要等待的事件的数量

  • timeout:等待事件发生的时间,以毫秒为单位。如果为 -1,表示一直等待,直到有事件发生。如果为 0,表示不阻塞,立即返回。

  • 返回值:如果有事件发生,返回值为正数,表示有多少个文件描述符发生了事件,超时则返回0,错误返回值为 -1。

struct pollfd的结构体:

1
2
3
4
5
6
7
8
9
struct pollfd{

 int fd; // 文件描述符

 short event;// 请求的事件

 short revent;// 返回的事件

}
  • fd:要监视的文件描述符,如果fd无效的话那么events监视事件也无效,并且revents返回0.

  • event:表示要监视的事件,可以监视的事件如下(不全):

    事件名 描述
    POLLIN 输入数据可读
    POLLOUT 输出缓冲区有空闲空间,可以写入数据
    POLLHUP 有指定的文件描述符挂起
    POLLPRI 输入数据有紧急数据可读(比如带外数据)
    POLLNVAL 无效的文件描述符
    POLLRDNORM 同 POLLIN
  • revents :返回的事件,由linux内核设置具体的返回事件

使用 poll 函数来等待标准输入(键盘输入)和一个文件描述符的变化

  • 初始化 pollfd 结构体:

    fds[0] 用于标准输入(STDIN_FILENO),关心可读事件(POLLIN)。
    fds[1] 用于打开的文件描述符,也关心可读事件(POLLIN)。

  • 调用 poll 函数:

    使用 while (1) 循环来持续调用 poll,以便持续监测文件描述符的状态。
    poll(fds, 2, 5000) 表示等待 fds 数组中的两个文件描述符在 5000 毫秒内变得可读。
    如果返回值为 -1,表示发生错误。
    如果返回值为 0,表示超时。
    如果返回值为正数,表示有文件描述符发生了事件。

  • 处理事件:

    检查 fds[0].revents 和 fds[1].revents,判断哪些文件描述符发生了事件。
    如果文件描述符可读,读取数据并打印。
    如果文件描述符对端关闭连接或发生错误,打印相应的信息。

完整代码

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main() {
struct pollfd fds[2];
int ret;
char buffer[1024];

// 初始化 pollfd 结构体
fds[0].fd = STDIN_FILENO; // 标准输入
fds[0].events = POLLIN; // 关心可读事件

// 打开一个文件
fds[1].fd = open("example.txt", O_RDONLY);
if (fds[1].fd == -1) {
perror("open");
return -1;
}
fds[1].events = POLLIN; // 关心可读事件

// 调用 poll 函数
while (1) {
ret = poll(fds, 2, 5000); // 等待 5000 毫秒

if (ret == -1) {
perror("poll");
close(fds[1].fd);
return -1;
} else if (ret == 0) {
printf("poll 超时\n");
} else {
// 当输入内容后,按下回车,就会有事件发生,并且输出标准输入的内容
if (fds[0].revents & POLLIN) {
printf("标准输入可读\n");
ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
printf("读取到: %s\n", buffer);
// 将文件描述符从可读改为不可读,当解开注释后,输入内容就会将文件描述符变为不可读
// fds[1].events = 0; // 不关心任何事件
}
}
if (fds[1].revents & POLLIN) {
printf("文件描述符可读\n");
//每次更新example.txt中的内容后才会输出example.txt的内容
ssize_t bytes_read = read(fds[1].fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
printf("从文件读取到: %s\n", buffer);
}
}
if (fds[0].revents & POLLHUP) {
printf("标准输入对端关闭连接\n");
}
if (fds[1].revents & POLLHUP) {
printf("文件描述符对端关闭连接\n");
}
if (fds[0].revents & POLLERR) {
printf("标准输入发生错误\n");
}
if (fds[1].revents & POLLERR) {
printf("文件描述符发生错误\n");
}
}
sleep(1);
}

close(fds[1].fd);
return 0;
}