1
第一个问题:timeslice用尽的判断
在Linux内核中,调度器确实是在时钟中断(通常每隔一段时间触发,比如1毫秒)中更新每个进程的vruntime值。
这个vruntime(虚拟运行时间)是CFS(完全公平调度器)用来衡量进程调度公平性的重要参数。
当时钟中断触发时,调度器会根据当前正在运行的进程计算其增量vruntime。
每个进程的vruntime增长速度是根据它的权重(权重越大,增长越慢)和时间片长度来确定的。
理论上,vruntime用来模拟每个进程在公平共享CPU时间时应该走过的路径。
在时钟中断中,调度器还会检查当前进程的时间片(timeslice)是否已经用完。
如果用尽,则意味着该进程已经占用了足够的CPU时间,调度器将会在红黑树(rb_tree,用于组织可运行进程的平衡二叉树)中寻找最左侧的下一个进程来运行。
这种查找最左节点的方式可以快速找到虚拟时间最小的进程,即最“欠公平”的进程。
因此,timeslice的用尽和时钟中断确实有紧密的联系:调度器利用定期的时钟中断来逐步更新进程的运行状态并判断时间片是否耗尽。
当时间片耗尽时,调度器会执行上下文切换,从红黑树中选择一个新的可运行进程。
2
第二个问题:可中断睡眠进程的唤醒
对于睡眠状态的进程,首先要明确睡眠类型和信号机制的工作原理。
在Linux中,进程的睡眠状态主要分为可中断睡眠(TASK_INTERRUPTIBLE)和不可中断睡眠(TASK_UNINTERRUPTIBLE)。
在可中断睡眠状态下,进程可以被外部事件(如信号)唤醒;而在不可中断睡眠中,除非事件完成,否则不能被信号中断。
当一个进程处于TASK_INTERRUPTIBLE状态时,它其实不会占用CPU资源。
睡眠状态的进程被挂起在一个等待队列上,调度器并不会安排它去运行。
但是,当有信号发送到该进程(比如SIGINT),或者某个条件变为真时,会触发wake_up操作,将该进程从TASK_INTERRUPTIBLE变为TASK_RUNNING状态,意味着该进程现在可运行了,并会重新被调度器考虑执行。
这里的关键点在于,虽然进程已经睡眠,但是内核中有相应的数据结构(如等待队列)来跟踪这些睡眠的进程。
当信号到来时,不是进程“主动”发现信号,而是内核中的信号处理机制“通知”调度器这个进程需要被唤醒。
唤醒后的进程会重新进入调度器的红黑树等待被调度执行。