I/O多路复用 select/poll/epoll 多路选择器的简单实例

博客介绍了I/O多路复用中的select、poll和epoll,强调了poll解决了select的最大文件描述符数量限制问题,但两者都需要遍历fd集合。epoll则通过回调机制、红黑树存储和内核事件通知提供更高的性能和扩展性,适合大规模并发连接。
摘要由CSDN通过智能技术生成

select & poll

#include <sys/select.h>
 
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
 
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
 
// 和 select 紧密结合的四个宏:
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

  select:

#include <stdio.h>
#include <poll.h>

int main() {
    struct pollfd fds[1];
    int timeout = 5000; // 设置超时时间为 5 秒

    fds[0].fd = 0; // 标准输入的文件描述符为 0
    fds[0].events = POLLIN; // 关注可读事件

    while (1) {
        int ready = poll(fds, 1, timeout); // 调用 poll 函数等待事件发生

        if (ready == -1) {
            perror("poll");
            break;
        } else if (ready == 0) {
            printf("Timeout\n");
        } else {
            // 检查标准输入是否就绪
            if (fds[0].revents & POLLIN) {
                char buffer[1024];
                int bytesRead = read(0, buffer, sizeof(buffer));
                if (bytesRead > 0) {
                    // 处理从标准输入读取的数据
                    printf("Read %d bytes: %.*s\n", bytesRead, bytesRead, buffer);
                } else if (bytesRead == 0) {
                    printf("EOF\n");
                    break;
                } else {
                    perror("read");
                    break;
                }
            }
        }
    }

    return 0;
}

  poll:

#include <stdio.h>
#include <poll.h>

int main() {
    struct pollfd fds[1];
    int timeout = 5000; // 设置超时时间为 5 秒

    fds[0].fd = 0; // 标准输入的文件描述符为 0
    fds[0].events = POLLIN; // 关注可读事件

    while (1) {
        int ready = poll(fds, 1, timeout); // 调用 poll 函数等待事件发生

        if (ready == -1) {
            perror("poll");
            break;
        } else if (ready == 0) {
            printf("Timeout\n");
        } else {
            // 检查标准输入是否就绪
            if (fds[0].revents & POLLIN) {
                char buffer[1024];
                int bytesRead = read(0, buffer, sizeof(buffer));
                if (bytesRead > 0) {
                    // 处理从标准输入读取的数据
                    printf("Read %d bytes: %.*s\n", bytesRead, bytesRead, buffer);
                } else if (bytesRead == 0) {
                    printf("EOF\n");
                    break;
                } else {
                    perror("read");
                    break;
                }
            }
        }
    }

    return 0;
}

        poll 的实现和 select 非常相似,只是描述 fd 集合的方式不同,poll 使用 pollfd 结构而不是 select 的 fd_set 结构,poll 解决了最大文件描述符数量限制的问题,但是同样需要从用户态拷贝所有的 fd 到内核态,也需要线性遍历所有的 fd 集合,所以它和 select 只是实现细节上的区分,并没有本质上的区别。

epoll:

epoll 的API

#include <sys/epoll.h>  
int epoll_create(int size); // int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>

int main() {
    int epoll_fd = epoll_create1(0); // 创建 epoll 实例
    if (epoll_fd == -1) {
        perror("epoll_create1");
        return 1;
    }

    struct epoll_event event;
    event.events = EPOLLIN; // 关注可读事件
    event.data.fd = 0; // 标准输入的文件描述符为 0

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event) == -1) {
        perror("epoll_ctl");
        close(epoll_fd);
        return 1;
    }

    struct epoll_event events[1];
    int max_events = 1;
    int timeout = 5000; // 设置超时时间为 5 秒

    while (1) {
        int ready = epoll_wait(epoll_fd, events, max_events, timeout); // 调用 epoll_wait 函数等待事件发生

        if (ready == -1) {
            perror("epoll_wait");
            break;
        } else if (ready == 0) {
            printf("Timeout\n");
        } else {
            // 检查标准输入是否就绪
            if (events[0].events & EPOLLIN) {
                char buffer[1024];
                int bytesRead = read(0, buffer, sizeof(buffer));
                if (bytesRead > 0) {
                    // 处理从标准输入读取的数据
                    printf("Read %d bytes: %.*s\n", bytesRead, bytesRead, buffer);
                } else if (bytesRead == 0) {
                    printf("EOF\n");
                    break;
                } else {
                    perror("read");
                    break;
                }
            }
        }
    }

    close(epoll_fd);

    return 0;
}

        通过代码实例更好的了解并区分他们之间的差异.

epoll 相较于select/poll 高性能的地方在哪?

epoll 相较于 selectpoll 具有以下几个性能优势:

  1. 事件通知方式selectpoll 在有事件发生时需要遍历整个文件描述符集合来找到就绪的描述符,而 epoll 利用回调机制,只返回就绪的文件描述符,避免了遍历整个集合的开销。

  2. 更高的扩展性selectpoll 的时间复杂度为 O(n),其中 n 是监视的文件描述符数量,随着文件描述符数量的增加,性能下降明显。而 epoll 使用红黑树(Red-Black Tree)来存储文件描述符,时间复杂度为 O(log n),具有更好的扩展性。

  3. 效率更高的内核通知机制selectpoll 需要通过轮询方式或定时器来检查文件描述符的状态变化,而 epoll 利用内核的事件通知机制(例如 epoll_wait),在文件描述符状态变化时直接由内核通知应用程序,避免了无效的轮询和定时器开销。

  4. 支持大规模并发:由于 epoll 使用红黑树存储文件描述符,因此可以支持大规模的并发连接。相比之下,selectpoll 对于大量文件描述符的集合来说性能较差。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值