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
        5
        struct pollfd {
        int fd; // 要监控的文件描述符
        *short events; //* 关注的事件位掩码
        *short revents; //* 内核返回的事件位掩码(由系统调用填写)
        };

        其中,events 常常使用宏来填写。这些表示事件的宏定义如下:

        值 (16 进制) 说明
        POLLIN 0x0001 文件可读(包括普通数据或高优先级数据)
        POLLPRI 0x0002 高优先级数据可读(例如带外数据)
        POLLOUT 0x0004 文件可写
        POLLERR 0x0008 错误条件发生(系统设置)
        POLLHUP 0x0010 对端挂断或 FIFO 文件关闭
        POLLNVAL 0x0020 文件描述符无效
        POLLRDNORM 0x0040 普通数据可读(类似 POLLIN
        POLLRDBAND 0x0080 带外/优先级数据可读
        POLLWRNORM 0x0100 普通可写(类似 POLLOUT
        POLLWRBAND 0x0200 带外/优先级数据可写
        POLLMSG 0x0400 消息可用(主要用于消息队列,POSIX)
        POLLREMOVE 0x1000 监控对象被移除(内核使用)
        POLLONESHOT 0x8000 仅触发一次事件,之后自动清除(内核使用)
      • 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。