轮询子例程

用途

检查多个文件描述符和消息队列的 I/O 状态。

标准 C 库 (libc.a)

语法

#include <sys/poll.h>
#include <sys/select.h>
#include <sys/types.h>
int poll( ListPointerNfdsmsgsTimeout)
void *ListPointer;
unsigned long Nfdsmsgs;
long Timeout;

描述

poll 子例程检查指定的文件描述符和消息队列,以查看它们是否已准备好进行读取 (接收) 或写入 (发送) ,或者查看它们是否具有异常情况暂挂。 尽管有 OPEN_MAX 个文件描述符可用,但只有 FD_SETSIZE 个文件描述符可通过此子例程访问。

注: poll 子例程仅适用于字符设备,管道,消息队列和套接字。 并非所有字符设备驱动程序都支持此操作。 请参阅各个字符设备的描述,以获取有关特定设备驱动程序是否以及如何支持 pollselect 子例程的信息。

为了与此操作系统的先前发行版和 BSD 系统兼容,还支持 select 子例程。

如果程序需要使用消息队列支持,那么必须使用 -D _MSGQSUPPORT 编译标志来编译程序源代码。

参数

表 1. 参数
描述
ListPointer 指定指向 pollfd 结构, pollmsg 结构或 pollist 结构的数组的指针。 每个结构指定文件描述符或消息队列标识以及此文件或消息队列的相关事件。 pollfd pollmsgpollist 结构在 /usr/include/sys/poll.h 文件中定义。 如果要使用 pollist 结构,那么将在用户程序中定义类似于以下 musr 的结构。 pollfd 结构必须在 pollmsg 结构之前。
struct pollist {
   struct pollfd fds[3];
   struct pollmsg msgs[2];
   } list;

然后可以按如下所示初始化结构

list.fds[0].fd = file_descriptorA;
list.fds[0].events = requested_events;
list.msgs[0].msgid = message_id;
list.msgs[0].events = requested_events;

可以采用相同的方式初始化 fdsmsgs 数组中的其余元素。 然后可以调用 poll 子例程,如下所示

nfds = 3;   /* number of pollfd structs */
nmsgs = 2;              /* number of pollmsg structs */
timeout = 1000          /* number of milliseconds to timeout */
poll(&list, (nmsgs<<16)|(nfds), 1000);

必须在计算 Nfdsmsgs 参数时使用 fdsmsgs 数组中的确切元素数。

Nfdsmsgs 指定要检查的文件描述符数和确切消息队列数。 低阶 16 位给出了 pollfd 结构数组中的元素数量,而高阶 16 位给出了 pollmsg 结构数组中的确切元素数量。 如果 Nfdsmsgs 参数的一半都等于值 0 ,那么假定对应的数组不存在。
超时 指定等待至少一个指定事件发生的最大时间长度 (以毫秒为单位)。 如果超时参数值为 -1 ,则 poll 子例程在至少一个指定事件发生之前不会返回。 如果 Timeout 参数的值为 0 ,那么 poll 子例程不会等待事件发生,但会立即返回,即使未发生任何指定事件也是如此。

互斥通知

如果多个进程轮询共享文件或消息队列,那么程序可能会迂到雷群问题。 当多个线程轮询同一事件并且必须唤醒每个线程以进行通知时,将发生雷群问题。 要防止此问题,程序必须通过在 requested events 字段中指定 POLLEXCL 标志来请求互斥通知。 在有关事件的通知期间,内核会选择并通知单个线程,该线程指定 POLLEXCL 标志以及来自多个线程的轮询请求。 在轮询同一事件时,将始终通知未指定 POLLEXCL 标志的线程。 pollpollset API 支持 POLLEXCL 标志。
注: 限制为单线程或多线程进程的程序可以使用 pollset API 来接收互斥通知。

对于将 POLLEXCL 标志用于有关事件的互斥通知的程序,下表描述了受支持的策略或行为。 这些策略确定为有关事件的通知选择了哪个线程。 还支持单个事件行为,以禁止从轮询请求返回的事件数。 例如,在负载均衡方案中,程序可能会发现此行为很有用。 当程序调用 pollset_pollpollset_poll_ext 子例程时,使用 pollset API 的程序可以通过指定数组长度 1 来实现相同的单个事件行为。 使用 POLLEXCL_POLICY 环境变量为每个进程设置所需的策略或行为。 某些策略和行为是互斥的。 每当多个策略之间发生冲突时。 将使用系统的缺省策略。

以下策略规范可能导致策略或行为发生冲突:
  • 该过程指定多个互斥策略。 例如, POLLEXCL_POLICY = RR:FIFO:ONE
  • 两个进程 A 和 B 指定不同的策略。 例如, POLLEXCL_POLICY=RR 和 POLLEXCL_POLICY=LIFO
注: POLLEXCL_POLICY=RR 是所有进程和线程的缺省策略。
下表列出了不同的策略或行为:
策略或行为 描述 相互排他性 缺省值
循环法 (RR) 使用旋转算法来选择通知的线程。 与 FIFO 和 LIFO 互斥。
first-in first-out(先进先出,FIFO) 将选择轮询时间最长的线程进行通知。 与 RR 和 LIFO 互斥。 False
last-in first-out(后进先出,LIFO) 将为通知选择轮询时间最少的线程。 与 RR 和 FIFO 互斥。 False
单个事件 (一个) 禁止从轮询返回的事件数。 不是互斥的。 False

轮询子例程 STREAMS 扩展

除了上述函数外, poll 子例程还通过一组引用开放式流的文件描述符对输入/输出进行多路复用。 poll 子例程标识可发送或接收消息的流,或发生特定事件的流。 

可以使用 read 子例程或 getmsg 系统调用来接收消息。 可以使用 write 子例程或 putmsg 系统调用来发送消息。 某些 streamio 操作 (例如 I_RECVFDI_SENDFD ) 也可用于发送和接收消息。 请参阅 streamio 操作。

ListPointer 参数指定要检查的文件描述符以及每个文件描述符的相关事件。 它指向一个数组,对于每个感兴趣的打开文件描述符,该数组都有一个元素。 数组的元素为 pollfd 结构。 除了 /usr/include/sys/poll.h 文件中的 pollfd 结构外, STREAMS 还支持以下成员:

int fd;             /*  file descriptor  */ short events;       /* requested events */ short revents;      /* returned events  */

fd 字段指定打开的文件描述符,并且 eventsrevents 字段是通过对下列事件标志的任意组合进行 OR 运算而构造的位掩码:

描述
POLLIN 流头读队列上存在非优先级或文件描述符传递消息。 即使消息的长度为 0 ,也会设置此标志。 在 revents 字段中,此标志与 POLLPRI 标志互斥。 请参阅 I_RECVFD 命令。
POLLRDNORM 在流头读队列上存在非优先级消息。
POLLRDBAND 在流头读队列上存在优先级消息 (频带 > 0)。
POLLPRI 在流头读队列上存在高优先级消息。 即使消息的长度为 0 ,也会设置此标志。 在 revents 字段中,此标志与 POLLIN 标志互斥。
POLLOUT 流中的第一个下游写队列未填满。 可以随时发送正常优先级消息。 请参阅 putmsg 系统调用。
POLLWRNORM POLLOUT相同。
POLLWRBAND 下游存在大于 0 的优先级频带,并且可以随时发送优先级消息。
POLLMSG 包含 SIGPOLL 信号的消息已到达流头读队列的前面。

返回值

成功完成时, poll 子例程返回一个值,该值指示满足选择标准的文件描述符和消息队列的总数。 返回值类似于 Nfdsmsgs 参数,因为低阶 16 位提供文件描述符的数量,高阶 16 位提供具有非零 revents 值的消息队列标识的数量。 在 sys/select.h 文件中找到的 NFDSNMSGS 宏可用于将这两个值与返回值分开。 NFDS 宏返回 NFDS#,其中返回的数字指示报告某个事件或错误的文件数,而 NMSGS 宏返回 NMSGS#,其中返回的数字指示报告某个事件或错误的消息队列数。

值 0 指示 poll 子例程超时,并且没有指定的文件或消息队列指示存在事件 (所有 revents 字段都是 0 的值)。

如果不成功,则返回值为 -1 ,并设置全局变量 errno 以指示错误。

错误代码

如果下列其中一个或多个为 true ,那么 poll 子例程不会成功运行:

表 2。 错误代码
描述
EAGAIN 内部数据结构分配失败。
EINTR poll 系统调用期间捕获到信号,并且已安装信号处理程序并指示不重新启动子例程。
EINVAL Nfdsmsgs 参数指定的 pollfd 结构数大于 FD_SETSIZE。 如果 Nfdsmsgs 参数指定的 pollmsg 结构数大于允许的最大消息队列数,那么也会返回此错误。
EFAULT ListPointer 参数与 Nfdsmsgs 参数一起寻址进程的已分配地址空间外部的位置。