POLL - Linux手册页

时间:2019-08-20 17:59:07  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-04-11

名称

poll,ppoll-等待文件描述符上的某些事件

语法

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <signal.h>
#include <poll.h>

int ppoll(struct pollfd *fds, nfds_t nfds,
        const struct timespec *tmo_p, const sigset_t *sigmask);

说明

poll()执行与select(2)类似的任务:它等待一组文件描述符中的一个准备就绪以执行I / O。特定于Linux的epoll(7)API执行类似的任务,但提供的功能超出poll()中的功能。

在fds参数中指定了要监视的文件描述符集,该参数是以下形式的结构数组:

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

调用方应在nfds中指定fds数组中的项目数。

字段fd包含打开文件的文件描述符。如果此字段为负,则将忽略相应的事件字段,而revents字段将返回零。 (这提供了一种忽略单个poll()调用的文件描述符的简单方法:只需对fd字段求反。但是请注意,该技术不能用于忽略文件描述符0。)

字段事件是一个输入参数,是一个位掩码,用于指定应用程序对文件描述符fd感兴趣的事件。该字段可以指定为零,在这种情况下,只能在清除中返回的事件是POLLHUP,POLLERR和POLLNVAL(请参见下文)。

字段revents是一个输出参数,由内核填充实际发生的事件。 revents中返回的位可以包括事件中指定的任何位,或值POLLERR,POLLHUP或POLLNVAL之一。 (这三个位在事件字段中是无意义的,只要相应条件为真,就会在revents字段中设置。)

如果对于任何文件描述符都没有发生所请求的事件(也没有错误),则poll()会阻塞直到其中一个事件发生。

timeout参数指定poll()在等待文件描述符准备就绪之前应阻塞的毫秒数。呼叫将一直阻塞,直到发生以下任何一种情况:

*
文件描述符准备就绪;
*
呼叫被信号处理器中断;要么
*
超时到期。

请注意,超时间隔将四舍五入为系统时钟的粒度,内核调度延迟意味着阻塞间隔可能会少量溢出。在超时中指定负值表示无限超时。将超时指定为零会导致poll()立即返回,即使没有准备好文件描述符也是如此。

在事件和清除中可以设置/返回的位定义在:

POLLIN
有要读取的数据。
POLLPRI
There is some exceptional condition on the file descriptor. Possibilities include:
*
TCP套接字上有带外数据(请参阅tcp(7))。
*
处于数据包模式的伪终端主机已发现从机上的状态发生了变化(请参阅ioctl_tty(2))。
*
cgroup.events文件已被修改(请参阅cgroups(7))。
POLLOUT
现在可以进行写操作,尽管写操作仍然会超出套接字或管道中的可用空间(除非设置了O_NONBLOCK)。
POLLRDHUP(since Linux 2.6.17)
流套接字对等方关闭连接,或关闭写入一半连接。为了获得此定义,必须定义_GNU_SOURCE功能测试宏(在包含任何头文件之前)。
POLLERR
错误条件(仅在revent中返回;在事件中忽略)。当读取端已关闭时,还会为引用管道的写入端的文件描述符设置此位。
POLLHUP
挂断(仅在revent中返回;在事件中忽略)。请注意,当从管道(例如管道或流套接字)读取时,此事件仅指示对等方关闭了其通道的末端。仅在使用完该通道中的所有未完成数据之后,从该通道进行的后续读取将返回0(文件末尾)。
POLLNVAL
无效的请求:fd无法打开(仅在revents中返回;在事件中被忽略)。

使用已定义的_XOPEN_SOURCE进行编译时,还具有以下内容,除了上面列出的位以外,它们不提供任何其他信息:

POLLRDNORM
相当于POLLIN。
POLLRDBAND
可以读取优先级带数据(在Linux上通常不使用)。
POLLWRNORM
相当于POLLOUT。
POLLWRBAND
可以写入优先级数据。

Linux也知道但不使用POLLMSG。

ppoll()

poll()和ppoll()之间的关系类似于select(2)和pselect(2)之间的关系:像pselect(2)一样,ppoll()允许应用程序安全地等待,直到文件描述符准备就绪或直到信号被捕获。

除了超时参数的精度差异外,以下ppoll()调用:

ready = ppoll(&fds, nfds, tmo_p, &sigmask);

几乎等同于原子执行以下调用:

sigset_t origmask;
int timeout;

timeout = (tmo_p == NULL) ? -1 :
          (tmo_p->tv_sec * 1000 + tmo_p->tv_nsec / 1000000);
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = poll(&fds, nfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);

上面的代码段几乎被描述为等效,因为poll()的负超时值被解释为无限超时,而* tmo_p中表示的负值会导致ppoll()错误。

有关为何需要ppoll()的说明,请参见pselect(2)的描述。

如果将sigmask参数指定为NULL,则不会执行信号屏蔽操作(因此ppoll()与poll()的区别仅在于超时参数的精度)。

tmo_p参数指定ppoll()将阻止的时间上限。此参数是指向以下形式结构的指针:

struct timespec {
    long    tv_sec;         /* seconds */
    long    tv_nsec;        /* nanoseconds */
};

如果将tmo_p指定为NULL,则ppoll()可以无限期阻塞。

返回值

成功后,poll()返回一个非负值,该值是pollfd中其revents字段已设置为非零值(指示事件或错误)的元素数。返回值为零表示在读取任何文件描述符之前系统调用已超时。

发生错误时,将返回-1,并设置errno以指示错误原因。

错误说明

EFAULT
fds指向进程的可访问地址空间之外。作为参数给出的数组未包含在调用程序的地址空间中。
EINTR
在任何请求的事件之前发生信号;参见signal(7)。
EINVAL
nfds值超过RLIMIT_NOFILE值。
EINVAL
(ppoll())以* ip表示的超时值无效(负)。
ENOMEM
无法为内核数据结构分配内存。

版本

在Linux 2.1.23中引入了poll()系统调用。在缺少此系统调用的较旧内核上,glibc poll()包装函数使用select(2)提供仿真。

ppoll()系统调用已添加到内核2.6.16中的Linux中。在glibc 2.4中添加了ppoll()库调用。

遵循规范

poll()符合POSIX.1-2001和POSIX.1-2008。 ppoll()是特定于Linux的。

备注

poll()和ppoll()的操作不受O_NONBLOCK标志的影响。

在其他一些UNIX系统上,如果系统无法分配内核内部资源而不是像Linux那样分配ENOMEM,则poll()可能会失败,并显示错误EAGAIN。 POSIX允许这种行为。可移植程序可能希望检查EAGAIN和循环,就像使用EINTR一样。

一些实现将值为-1的非标准常量INFTIM定义为poll()的超时值。 glibc中未提供此常数。

有关在另一个线程中关闭由poll()监视的文件描述符可能发生的情况的讨论,请参见select(2)。

C library/kernel differences

Linux ppoll()系统调用会修改其tmo_p参数。但是,glibc包装函数通过将局部变量用于传递给系统调用的超时参数来隐藏此行为。因此,glibc ppoll()函数不会修改其tmo_p参数。

原始ppoll()系统调用具有第五个参数size_t sigsetsize,它指定sigmask参数的字节大小。 glibc ppoll()包装函数将此参数指定为固定值(等于sizeof(kernel_sigset_t))。有关内核和sigset的libc概念之间的区别的讨论,请参见sigprocmask(2)。

BUGS

请参见select(2)的" BUGS"部分下的有关杂散就绪通知的讨论。

示例

下面的程序打开在其命令行参数中命名的每个文件,并监视生成的文件描述符以使其易于读取(POLLIN)。程序反复循环,使用poll()监视文件描述符,并在返回时打印就绪文件描述符的数量。对于每个就绪文件描述符,该程序:

*
以人类可读的形式显示返回的revents字段;
*
如果文件描述符是可读的,则从文件描述符中读取一些数据,并在标准输出上显示该数据;和
*
如果文件描述符不可读,但发生了其他事件(大概是POLLHUP),则关闭文件描述符。

假设我们在一个终端中运行该程序,要求它打开一个FIFO:

$ mkfifo myfifo
$ ./poll_input myfifo

在第二个终端窗口中,然后打开FIFO进行写入,向其中写入一些数据,然后关闭FIFO:

$ echo aaaaabbbbbccccc > myfifo

在运行程序的终端中,我们将看到:

Opened "myfifo" on fd 3
About to poll()
Ready: 1
  fd=3; events: POLLIN POLLHUP
    read 10 bytes: aaaaabbbbb
About to poll()
Ready: 1
  fd=3; events: POLLIN POLLHUP
    read 6 bytes: ccccc

About to poll()
Ready: 1
  fd=3; events: POLLHUP
    closing fd 3
All file descriptors closed; bye

在上面的输出中,我们看到poll()返回了三次:

*
在第一次返回时,在revents字段中返回的位是POLLIN(表明文件描述符是可读的)和POLLHUP(表明FIFO的另一端已关闭)。然后,程序消耗了一些可用的输入。
*
poll()的第二次返回还指示POLLIN和POLLHUP;然后,程序将消耗最后一个可用输入。
*
在最后的返回中,poll()仅在FIFO上指示POLLHUP,这时文件描述符关闭,程序终止。

Program source

/* poll_input.c

   Licensed under GNU General Public License v2 or later.
*/
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

int
main(int argc, char *argv[])
{
    int nfds, num_open_fds;
    struct pollfd *pfds;

    if (argc < 2) {
       fprintf(stderr, "Usage: %s file...\n", argv[0]);
       exit(EXIT_FAILURE);
    }

    num_open_fds = nfds = argc - 1;
    pfds = calloc(nfds, sizeof(struct pollfd));
    if (pfds == NULL)
        errExit("malloc");

    /* Open each file on command line, and add it aqpfdsaq array */

    for (int j = 0; j < nfds; j++) {
        pfds[j].fd = open(argv[j + 1], O_RDONLY);
        if (pfds[j].fd == -1)
            errExit("open");

        printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

        pfds[j].events = POLLIN;
    }

    /* Keep calling poll() as long as at least one file descriptor is
       open */

    while (num_open_fds > 0) {
        int ready;

        printf("About to poll()\n");
        ready = poll(pfds, nfds, -1);
        if (ready == -1)
            errExit("poll");

        printf("Ready: %d\n", ready);

        /* Deal with array returned by poll() */

        for (int j = 0; j < nfds; j++) {
            char buf[10];

            if (pfds[j].revents != 0) {
                printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                        (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                        (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                        (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                if (pfds[j].revents & POLLIN) {
                    ssize_t s = read(pfds[j].fd, buf, sizeof(buf));
                    if (s == -1)
                        errExit("read");
                    printf("    read %zd bytes: %.*s\n",
                            s, (int) s, buf);
                } else {                /* POLLERR | POLLHUP */
                    printf("    closing fd %d\n", pfds[j].fd);
                    if (close(pfds[j].fd) == -1)
                        errExit("close");
                    num_open_fds--;
                }
            }
        }
    }

    printf("All file descriptors closed; bye\n");
    exit(EXIT_SUCCESS);
}

另外参见

restart_syscall(2),select(2),select_tut(2),epoll(7),time(7)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/