select

select 函数及 poll 函数族的主要功能用于等待(监视)一批(多个)文件描述符的状态变化,可读可写或者发生异常。这样做的话,就能保证处理一批文件描述符时,得以等待最少的时间快速响应。不然线程(Linux中的 task , SylixOS 中的thread )只能自己无阻塞轮询多个文件描述符,浪费大量时间,而这些工作由内核处理更好一些,这就是 select 的目的和工作原理。

Linux 的 select 调用最终由 poll 实现,而 poll 将为每个需要监视的文件描述符建立等待节点,调用驱动实现的 poll 函数。驱动实现的 poll 函数将给定的等待节点加入自己的读写(根据类型分别加入)队列中,并返回当前状态变化(并记录)。在第一次扫描文件描述符时,如果有任何感兴趣的状态发生,则返回,并传输文件描述符状态到用户空间。如果没有,那么线程将进入睡眠状态(当然,在调用 select 类似函数时传递的超时便在此时发生作用)。

如果在线程睡眠的时候文件描述符发生变化,线程将会通过驱动的 poll 函数添加的等待节点得以唤醒。唤醒之后的线程将重新调用所有的文件描述符对应的驱动的 poll 函数,并记录文件描述符状态并返回。在返回之前,将释放所有为文件系统建立的等待节点( epoll 可能不同),并传输文件描述符对应的状态。

总结一下,就是:为每个文件描述符建立等待节点,然后由设备唤醒,线程主动调用 poll 扫描状态。

为每个文件描述符建立等待节点,这意味着,线程同时加入多个等待队列。而它可能会在运行状态下被事件唤醒,而 Linux 的唤醒函数 try_to_wake_up 恰好支持唤醒一个可能已经苏醒的线程。而为了防止在扫描文件描述符时,被扫描过的文件描述符唤醒而错失时机,每此执行 poll 系统调用时,建立的相关结构体中有一个 triggered 标志变量。每当唤醒一次,就会设置它为 true ,在陷入睡眠之前需要检查这个标志。

SylixOS 与此不同,它的驱动模型没有 poll 函数,所以不能由内核设置文件描述符状态变化。它的工作机制为:每个执行的 select 的线程有一个信号量和 select 上下文(用于存放状态变化),扫描文件描述符时,驱动将记录必要信息,让驱动在能事件发生变化时在它的 select 上下文中设置事件发生,并通过信号量唤醒它。

这里同样有一个问题,那就是:在扫描文件描述符时,之前被扫描过的文件描述符发生变化了,怎么处理才能不会错失时机而陷入睡眠等待。与 Linux ttwu 不同, SylixOS 在 select 中使用的二值信号量将会保存事件发生状态,执行 pend 时会经过判断之后才决定是否陷入睡眠。在功能上与此 Linux 的 triggered 一致。

总结一下,就是:多个不同文件描述符通过一个信号量唤醒线程, SylixOS 设置文件描述符状态是由驱动实现的。

由于 Linux 设置和检查 triggered 是发生在 schedule 函数外面,所以,虽然在扫描完文件描述符后会检查 triggered 变量,但线程还是可能会在检查完 triggered 之后,实际睡眠之前被 trigger ,但此时已经错失时机。当然,错失时机之后也不会陷入睡眠, ttwu 将改变线程的运行标志,这将阻止陷入睡眠,并且改变标志必须进入 schedule 保护中(就绪队列锁, schedule 函数也是通过它来保护)。虽然如此,线程也将浪费一次调度周期,不能及时响应事件变化。而 SylixOS 由于设置和检测信号量都是在内核锁中,而内核锁也将保护调度,所以在这个地方它能及时响应事件变化。

参考: Linux 4.18.7、SylixOS 3.9.5、LDD3

PS:用类似 L4Linux 的思想,将 SylixOS 内核核心给刨掉,用微内核接口替代 SylixOS 的信号量, select 正常工作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值