I/O复用--poll

poll

poll(2) - Linux manual page (man7.org)

函数原型

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

poll对于select而言,简单很多。

  • pollfd结构体
/* Data structure describing a polling request.  */
struct pollfd
{
    int fd;			/* File descriptor to poll.  */
    short int events;		/* Types of events poller cares about.  */
    short int revents;		/* Types of events that actually occurred.  */
};
  • fd:要监听的文件描述
  • events:要监听的事件
  • revents:fd发生的事件
  • nfds:指明fds数组大小
typedef unsigned long int nfds_t;
  • timeout:指明超时时间,单位为毫秒

The timeout argument specifies the number of milliseconds that poll() should block waiting for a file descriptor to become ready. The call will block until either:

• a file descriptor becomes ready;

• the call is interrupted by a signal handler; or

• the timeout expires.

Note that the timeout interval will be rounded up to the system clock granularity, and kernel scheduling delays mean that the
blocking interval may overrun by a small amount. Specifying a negative value in timeout means an infinite timeout. Specifying
a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.

上面指明timeout参数单位为毫秒、poll阻塞和返回的原因以及指定的timeout会根据系统来调整。

图来自:(221条消息) poll函数详解_青季的博客-CSDN博客_poll函数

  • 返回值

    • 成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0;

    • 失败时,poll() 返回 -1,并设置 errno 为下列值之一:

      • EBADF:一个或多个结构体中指定的文件描述符无效。

      • EFAULT:fds 指针指向的地址超出进程的地址空间。

      • EINTR:请求的事件之前产生一个信号,调用可以重新发起。

      • EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。

      • ENOMEM:可用内存不足,无法完成请求。

poll事件

这里只摘抄了一些常用的事件,其余参考man手册。

  • POLLIN:
    • There is data to read.
    • 有数据可以读
  • POLLOUT:
    • Writing is now possible, though a write larger than the available space in a socket or pipe will still block (unless O_NONBLOCK is set).
    • 可写
  • POLLRDHUP (since Linux 2.6.17):
    • Stream socket peer closed connection, or shut down writing half of connection.
    • 流socket连接的对端close connection,或者只close了写连接
  • POLLERR:
    • Error condition (only returned in revents; ignored in events). This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed.
    • 出错或者尝试向管道写入数据,而这个管道的读端已关闭。
  • POLLHUP:
    • Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
    • 挂起

图来自:(221条消息) poll函数详解_青季的博客-CSDN博客_poll函数

函数使用

相比于select,poll没有最大文件描述符的限制。但poll也有一些弊端,就是需要遍历传入的fds数组的revents去一个个检查是否有事件发生.

以下这个例子是监听listenfd的连接请求以及其收到的数据

  • server
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[]) {
    nfds_t nfds;
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(30000);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int ret = bind(listenfd, (struct sockaddr*)&addr, sizeof(addr));
    ret = listen(listenfd, 5);
    struct pollfd pollfds[100];
    int user_cnt = 0;
    pollfds[0].fd = listenfd;
    pollfds[0].events = POLLIN;
    while(1) {
        int ready = poll(pollfds, user_cnt + 1, -1);
        for(int i = 0; i < user_cnt + 1; ++i) {
            if(pollfds[i].fd == listenfd) {
                struct sockaddr_in cli_addr;
                socklen_t len = sizeof(addr);
                int cli_fd = accept(listenfd, (struct sockaddr*)&cli_addr, &len);
                printf("new client %d connect\n", cli_fd);
                user_cnt++;
                pollfds[user_cnt].fd = cli_fd;
                pollfds[user_cnt].events = POLLIN;
            }
            else {
                char buf[1024];
                int ret = read(pollfds[i].fd, buf, sizeof(buf) - 1);
                printf("data from cilent %d : %s\n", pollfds[i].fd, buf);
            }
        }
    }
    return 0;
}

  • client
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>

int main(int argc, char const *argv[]) {
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(30000);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));

    while(1) {
        char buf[1024];
        scanf("%s", buf);
        ret = write(sockfd, buf, strlen(buf));
    }

    return 0;
}

底层原理

(221条消息) 144 POLL底层机制剖析_你板子冒烟了的博客-CSDN博客_vfs_poll

posted @ 2023-01-10 14:33  DavidJIAN  阅读(50)  评论(0)    收藏  举报