IO多路复用,select、poll和epoll简介

前言

select、poll 和 epoll 是 Linux 下用于多路复用 I/O(Input/Output)的系统调用,它们用于监视多个文件描述符,以查看哪个文件描述符上有可读、可写或发生了异常的事件。

1、select

select 是最早的多路复用 I/O 机制之一。它允许你监控多个文件描述符,以检测哪些文件描述符有事件发生。它通过修改一个位掩码来表示每个文件描述符的状态。

  • 使用方法
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    fd_set read_fds;
    int max_fd;
    struct timeval timeout;
    
    // 设置文件描述符集合
    FD_ZERO(&read_fds);
    FD_SET(STDIN_FILENO, &read_fds);
    max_fd = STDIN_FILENO;
    
    // 设置超时时间
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    
    int ret = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
    
    if (ret == -1) {
        perror("select");
        exit(EXIT_FAILURE);
    } else if (ret == 0) {
        printf("Timeout occurred!\n");
    } else {
        if (FD_ISSET(STDIN_FILENO, &read_fds)) {
            printf("Data is available on stdin.\n");
        }
    }
    
    return 0;
}

  • select的几个宏
/*
用于将文件描述符添加到文件描述符集合中
fd:要添加的文件描述符。
fdset:要修改的文件描述符集合。
*/
void FD_SET(int fd, fd_set *fdset);

/*
用于从文件描述符集合中删除指定的文件描述符
fd:要删除的文件描述符。
fdset:要修改的文件描述符集合。
*/
void FD_CLR(int fd, fd_set *fdset);

/*
用于检查文件描述符集合中是否包含指定的文件描述符
fd:要检查的文件描述符。
fdset:要检查的文件描述符集合。
*/
int FD_ISSET(int fd, fd_set *fdset);

/*
用于清空文件描述符集合
fdset:要清空的文件描述符集合。
*/
void FD_ZERO(fd_set *fdset);

  • select函数的五个参数含义
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds: 监控的最大文件描述符值加1。
readfds: 监控可读事件的文件描述符集合。
writefds: 监控可写事件的文件描述符集合。
exceptfds: 监控异常事件的文件描述符集合。
timeout: 超时时间,指定 select 需要等待的时间。如果为 NULL,则 select 会一直等待直到有事件发生。

  • 优缺点分析

(1)优点
简单: select 的接口简单易用,广泛支持。
兼容性: 几乎所有 UNIX-like 操作系统都支持 select。
(2)缺点
文件描述符限制: 默认情况下,select 的文件描述符数量限制为 1024(可以通过重新编译内核或修改宏 FD_SETSIZE 增加,但不建议)。
性能瓶颈: 每次调用 select 都需要将文件描述符集合从用户态复制到内核态,这在文件描述符数量很大时可能会带来性能问题。
线性扫描: select 需要线性扫描文件描述符集合,效率较低。

2、poll

poll 是 select 的改进版,功能和 select 类似,但解决了一些 select 的局限性。poll 使用一个结构体数组来表示文件描述符及其相关的事件,而不是使用位掩码。

  • 使用方法
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    struct pollfd fds;
    int timeout = 5000; // 超时时间:5秒

    fds.fd = STDIN_FILENO;
    fds.events = POLLIN;
    fds.revents = 0;

    int ret = poll(&fds, 1, timeout);

    if (ret == -1) {
        perror("poll");
        exit(EXIT_FAILURE);
    } else if (ret == 0) {
        printf("Timeout occurred!\n");
    } else {
        if (fds.revents & POLLIN) {
            printf("Data is available on stdin.\n");
        }
    }

    return 0;
}

  • poll函数的参数
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds: pollfd 结构体数组,每个结构体表示一个文件描述符及其事件。
nfds: pollfd 结构体数组的大小。
timeout: 超时时间,单位是毫秒。如果设置为 -1,poll 将无限期等待直到有事件发生;如果设置为 0,poll 将立即返回。

  • pollfd结构体
struct pollfd {
    int fd;         // 文件描述符
    short events;   // 监控的事件类型
    short revents;  // 实际发生的事件
};

  • 事件常量

POLLIN: 文件描述符可读(例如,有数据可读或连接可接受)。
POLLOUT: 文件描述符可写(例如,缓冲区有足够空间)。
POLLERR: 文件描述符发生错误。
POLLHUP: 文件描述符挂起(通常用于检测连接的关闭)。
POLLNVAL: 文件描述符无效(例如,关闭了的文件描述符)。

  • 优缺点分析

(1)优点
文件描述符数量无上限: 没有像 select 那样的文件描述符数量限制。
灵活性: 可以同时监控多个文件描述符及其事件。
(2)缺点
性能瓶颈: 每次调用 poll 时都需要遍历 pollfd 数组,这在监控大量文件描述符时会带来性能开销。

3、epoll

4、总结

select 适合处理少量文件描述符和需要跨平台支持的场景。它简单易用,但有文件描述符数量限制,并且在文件描述符数量多时性能较差。
poll 适合需要监控大量文件描述符的场景。它没有文件描述符数量限制,但在大量文件描述符时性能依然较差。

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不适合写代码的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值