找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 66|回复: 0
打印 上一主题 下一主题
收起左侧

select poll, epoll

[复制链接]
跳转到指定楼层
楼主
ID:1154223 发表于 2025-6-16 19:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Select,Poll,Epoll是学习I/O多路复用必不可少的一节,这章我将借用对这三个系统调用的讲解,进一步加深对于I/O多路复用的理解
首先我们要知道为什么要有I/O多路复用,可以通过一次系统调用,检查多个文件描述符的状态。这是 I/O 多路复用的主要优点,相比于非阻塞 I/O,在文件描述符较多的场景下,避免了频繁的用户态和内核态的切换,减少了系统调用的开销。
I/O 多路复用相当于将「遍历所有文件描述符、通过非阻塞 I/O 查看其是否就绪」的过程从用户线程移到了内核中,由内核来负责轮询。
首先回忆一下,I/O多路复用是一种使用少数线程来监听多数网络Socket的一种I/O方法,那么我们怎么使用I/O多路复用呢
Select
select使用一个固定大小的位图来表示文件描述符fd的集合,调用select检查这些fd的状态,每次调用select时都会重新构建位图,并将其传递给内核,内核来判断是否有I/O已经就绪
select的核心数据结构是一个fd_set,        这是一个文件描述符集合,用来管理需要监视的文件描述符
fd_set的核心是一个位图(大小位1024),每一位对应着文件描述符的状态,1表示该描述符需要监视,0表示该描述符不需要监视
typedef __kernel_fd_set                fd_set;

#define __FD_SETSIZE        1024

typedef struct {
        unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} __kernel_fd_set;
        select的函数原型如下
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

        可以看到,有三种fd_set
● readfds -- 可读事件,用来监视事件是否可读
● writefds -- 可写事件,用来监视事件是否可写
● exceptfds -- 描述符是否有异常情况
● nfds -- 要监视的文件描述符的最大值+1
● timeout -- 可以选择的监视时间 可以是 阻塞(NULL),立即返回(0),或者指定的等待时间
1. 在调用select的是否我们需要把需要监视的事件通过函数加入到对应的队列中
2. 进入内核态进行检查,程序在调用select之后,内核会历遍对应的fd查看是否符合对应的状态
  a. 符合:如果符合就把事件加入到readfds当中去
  b. 不符合:如果所有的都不符合就根据timeout来选择等待的方式和事件
3. 返回,最后会返回符合要求的fd的数量
    while (1) {      
        fd_set rfds;
        fd_set wfds;
        int32_t maxfd = 0, res = 0;
        struct timeval timeout;
        
        timeout.tv_sec = 0;
        timeout.tv_usec = 500;
        
        FD_ZERO(&rfds);
        FD_ZERO(&wfds);
        
        FD_SET(socket1, &rfds);
        FD_SET(socket2, &rfds);
        
        maxfd = socket1 > socket2 ? socket1 : socket2;
        
        res = select(maxfd + 1, &rfds, NULL, NULL, &timeout);
        
        if (res < 0 && errno != EINTR && errno != 0) {
            // log it
            return;
        }
        
        if (FD_ISSET(socket1, &rfds)) {
            // do something
        }
        if (FD_ISSET(socket2, &rfds)) {
            // do something
        }
     }
        上面大致讲解了如何使用select,这里其实我们可以很明显的看出一个缺点的,
1. 就是我们并不返回符合要求的fd,而是把所有的fd都返回,所以返回到用户态之后我们要进行fd的历遍最终才能找到有相应的fd,这显然是比较耗费事件的
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表