当前位置:首页 > C > 正文

掌握高效I/O处理(C语言poll函数详解)

C语言 编程中,当我们需要同时监听多个文件描述符(如套接字)的 I/O 事件时,使用传统的阻塞 I/O 模型会非常低效。这时候,poll 函数就派上用场了。本文将带你从零开始,深入浅出地学习如何使用 poll 函数实现高效的 IO多路复用,特别适合刚接触 网络编程 的小白开发者。

什么是 poll 函数?

poll 是 Linux/Unix 系统提供的一个系统调用,用于监视多个文件描述符,等待其中任意一个变为“就绪”状态(比如可读、可写或发生异常)。与 select 相比,poll 没有文件描述符数量限制(理论上),且接口更简洁。

掌握高效I/O处理(C语言poll函数详解) C语言 poll函数 IO多路复用 网络编程 第1张

poll 函数原型

首先,我们需要包含头文件:

#include <poll.h>  

poll 的函数声明如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);  
  • fds:指向 struct pollfd 数组的指针,每个元素代表一个要监视的文件描述符及其关注的事件。
  • nfds:数组中元素的个数。
  • timeout:超时时间(单位:毫秒)。设为 -1 表示永久阻塞;0 表示立即返回(非阻塞);正数表示最多等待多少毫秒。

struct pollfd 结构体详解

每个 pollfd 结构体定义如下:

struct pollfd {    int   fd;         /* 文件描述符 */    short events;     /* 等待的事件(输入) */    short revents;    /* 实际发生的事件(输出) */};  

常用事件标志包括:

  • POLLIN:数据可读
  • POLLOUT:可以写入数据
  • POLLERR:发生错误
  • POLLHUP:连接挂起(如对端关闭)

简单示例:监听标准输入和一个 TCP 套接字

下面是一个使用 poll 同时监听标准输入(stdin)和一个 TCP 客户端套接字的完整例子:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <poll.h>#include <string.h>int main() {    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (sockfd < 0) {        perror("socket");        exit(1);    }    struct sockaddr_in server_addr;    memset(&server_addr, 0, sizeof(server_addr));    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(8080);    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {        perror("connect");        close(sockfd);        exit(1);    }    struct pollfd fds[2];    fds[0].fd = STDIN_FILENO;   // 监听标准输入    fds[0].events = POLLIN;    fds[1].fd = sockfd;        // 监听套接字    fds[1].events = POLLIN;    char buffer[1024];    while (1) {        int ret = poll(fds, 2, -1); // 永久阻塞,直到有事件发生        if (ret < 0) {            perror("poll");            break;        }        if (fds[0].revents & POLLIN) {            // 标准输入有数据            ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer)-1);            if (n > 0) {                buffer[n] = '\0';                printf("你输入了: %s", buffer);            }        }        if (fds[1].revents & POLLIN) {            // 套接字有数据可读            ssize_t n = read(sockfd, buffer, sizeof(buffer)-1);            if (n > 0) {                buffer[n] = '\0';                printf("服务器消息: %s", buffer);            } else if (n == 0) {                printf("服务器已关闭连接\n");                break;            }        }    }    close(sockfd);    return 0;}  

poll 的优缺点

优点:

  • 没有文件描述符数量限制(不像 select 通常限制为 1024)
  • 每次调用不需要重置整个监听集合(select 需要)

缺点:

  • 在大量文件描述符中只有少量活跃时,性能仍较差(因为每次都要线性扫描所有 fd)
  • 跨平台兼容性不如 select(Windows 不支持 poll

总结

通过本文,你应该已经掌握了 C语言 中 poll 函数的基本用法。它是实现 IO多路复用 的重要工具之一,在高并发 网络编程 场景中非常实用。虽然现代 Linux 系统更推荐使用 epoll,但理解 poll 是迈向高性能 I/O 编程的重要一步。

现在,你可以尝试自己编写一个简单的聊天客户端或服务器,用 poll 来同时处理多个连接!