sys_poll (即调用号为7的poll系统调用)是 Linux 中一个非常重要的系统调用,用于检测多个文件描述符,看它们是否准备好去进行某种类型的 I/O 操作(比如是否可读、可写等)。它是一种实现 I/O 多路复用的高效方法,允许一个程序同时等待多个文件描述符,而不需要为每个文件描述符创建一个单独的线程。
sys_poll函数原型
1
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数
fds: 指向struct pollfd数组的指针,每个元素作为一个结构体描述一个文件描述符及其关心的事件。这个结构体本身被定义为:1
2
3
4
5struct pollfd {
int fd; // 要监控的文件描述符
*short events; //* 关注的事件位掩码
*short revents; //* 内核返回的事件位掩码(由系统调用填写)
};其中,
events常常使用宏来填写。这些表示事件的宏定义如下:宏 值 (16 进制) 说明 POLLIN0x0001文件可读(包括普通数据或高优先级数据) POLLPRI0x0002高优先级数据可读(例如带外数据) POLLOUT0x0004文件可写 POLLERR0x0008错误条件发生(系统设置) POLLHUP0x0010对端挂断或 FIFO 文件关闭 POLLNVAL0x0020文件描述符无效 POLLRDNORM0x0040普通数据可读(类似 POLLIN)POLLRDBAND0x0080带外/优先级数据可读 POLLWRNORM0x0100普通可写(类似 POLLOUT)POLLWRBAND0x0200带外/优先级数据可写 POLLMSG0x0400消息可用(主要用于消息队列,POSIX) POLLREMOVE0x1000监控对象被移除(内核使用) POLLONESHOT0x8000仅触发一次事件,之后自动清除(内核使用) nfds: 传入的数组中元素的数量timeout: 等待事件的超时时间(毫秒),-1 表示无限等待,0 表示立即返回
返回值
- 成功时返回发生事件的文件描述符数量(非负整数)
- 失败时返回
-errno(如EINVAL,EFAULT,EBADF)
用例
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.equ POLLIN, 0x0001
.section .data
fds:
# 定义一个 pollfd 数组,监控文件描述符 0 (stdin) 是否可读
.long 0 # fd = 0 (stdin)
.short POLLIN # events = POLLIN (可读事件)
.short 0x0000 # revents (内核填写,初始为 0)
timeout:
.long 5000 # 等待 5000 毫秒(5 秒)
.section .text
.globl _start
_start:
# 调用 sys_poll 监控 stdin 是否有可读事件
mov $7, %rax # 系统调用号 (7 = sys_poll)
lea fds(%rip), %rdi # struct pollfd *fds
mov $1, %rsi # nfds = 1 (数组中只有一个元素)
lea timeout(%rip), %rdx # timeout = 5000 ms
syscall # 执行系统调用
# 退出程序
mov $60, %rax # 系统调用号 (60 = sys_exit)
xor %rdi, %rdi # return 0
syscall分析:
上述代码演示了如何使用
sys_poll监控标准输入(stdin,文件描述符0)是否可读。程序首先在
.data段定义了一个struct pollfd数组fds:fd字段设为0,表示标准输入events字段设为POLLIN,表示关注可读事件revents字段初始化为0,系统调用返回时内核会写入实际发生的事件
timeout字段被设为5000,表示最多等待 5 秒。由于我们只定义了一个结构体,fds仅仅是一个指向结构体的指针,而不指向一个数组,所以我们将数组长度nfds设置为1。这样一来,运行到第一个syscall时,系统调用大致上可以等效为:1
poll(&fds, 1, 5000);
程序最后调用
sys_exit系统调用正常退出,返回值0表示成功。通过这种方式,
sys_poll可以有效地在单线程中同时监控多个 I/O 事件,常用于实现事件循环或非阻塞 I/O。