select源码分析

本文详细分析了Linux内核中select函数的工作原理,从select()开始,探讨了core_sys_select函数如何处理文件描述符,以及do_select函数如何进行消息轮询和等待。select函数的时间复杂度为O(n^3),随着文件描述符数量增加,效率下降,适用于小规模的文件描述符监控。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select源码分析


select()

函数是从SYSCALL_DEFINE5(select, ...)开始. 可以简单的将SYSCALL_DEFINEx理解为系统定义的系统函数, 如果想了解 SYSCALL_DEFINE 可以看一下.

具体的执行流程是 :

  • 将时间定义从用户空间复制到内核空间中, 进行时间片的设置, 如果为0或不合法就设置为默认值, 否则就设置为传入的时间.
  • 调用core_sys_select函数, 以实现等待消息到来, 轮询等主要操作
  • 调用timeval_compare返回执行完剩余的时间
  • 最后将返回的时间使用copy_to_user复制到用户空间
SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, fd_set __user *, exp, struct timeval __user *, tvp)
{
    s64 timeout = -1;
    struct timeval tv;
    int ret;

    if (tvp)
    {
        // 将数据从用户空间拷贝到内核空间的tv中
        if (copy_from_user(&tv, tvp, sizeof(tv)))
            return -EFAULT;
        ...
    }

    // 设置 fds 结构的参数并且等待消息的到来, 或者时间片没有结束调度程序
    ret = core_sys_select(n, inp, outp, exp, &timeout);

    // 设置时间片
    if (tvp)
    {
        ...
        // 返回执行完后剩余时间
        if (timeval_compare(&rtv, &tv) >= 0)
            rtv = tv;
        if (copy_to_user(tvp, &rtv, sizeof(rtv))) 
        ...
    }
    return ret;
}

core_sys_select

因为select的主要功能都是do_select函数, 这里我们先分析一下关于core_sys_select函数.

  • 获取文件文件描述符表并存放在fdtable
  • fdtable读, 写, 错误分配空间, 并初始化
  • 将select传入的readfds,writefds, errorfds参数从用户空间复制到内核空间的fdtable对应的读, 写, 错误中. 这里需要解释一下, fdselect主要是保存之后要将来的信号返回给用户空间的.
  • 调用do_select, 轮询等待消息的到来, 并且将消息的文件描述符保存在fds结构体中
  • fds保存的读, 写, 错误集合从内核空间复制到用户空间
// 参数满足 int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout); 
### 关于 `select` 函数源码分析 #### 主要函数调用链 `select` 的核心实现涉及多个层次的函数调用,具体路径如下: - 用户空间调用 `select()` 后进入内核模式。 - 进入内核后依次经过 `kern_select -> core_sys_select -> do_select` 处理流程[^1]。 #### 数据结构解析 ##### 文件描述符集合 (`fd_set`) `fd_set` 是一种位图数据结构,用来表示一组文件描述符的状态。每个文件描述符占用一位来标记其状态(0 或 1)。为了处理不同类型的 I/O 操作,通常会有三组这样的集合分别代表读、写和异常条件监视的需求。 ```c typedef struct { unsigned long fds_bits[NFDBITS/sizeof(long)]; } fd_set; ``` 当应用程序准备调用 `select` 前,需先初始化这些集合并设置待监测的具体文件描述符。由于 `select` 执行过程中可能会修改输入参数所指向的数据结构,因此建议开发者维护一份原始列表副本以便后续重置或验证操作[^2]。 #### 内部工作机理 在内部实现上,Linux 使用了一个名为 `stack_fds` 的辅助数组来暂存六个不同的位图信息——即三个初始传入的监听请求集及其对应的返回结果集。这种设计使得即使某些文件描述符未触发任何事件,在下一轮轮询之前仍能方便地恢复到之前的配置状态[^3]。 ```c struct stack_fds { fdset_t *in; /* Input sets */ fdset_t *out; /* Output sets (results) */ }; ``` 每次调用 `select` 都意味着一次完整的扫描过程:检查所有指定范围内的文件描述符是否有符合条件的变化发生,并更新相应的输出集合供应用层查询使用。 #### 实际应用场景下的注意事项 考虑到性能因素,频繁调用 `select` 可能带来额外开销,特别是在大量并发连接场景中表现尤为明显。此时可以考虑采用更高效的替代方案如 `epoll` 来优化网络编程模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值