先列个框架,后续丰富完善。
提这个问题,是因为在Linux内核学习中,遇到过一个让人啼笑皆非的问题。这个问题是啥呢?提个引子,在实际生活中,可能每个人都有过这样的经历,就是总是将某两个明明完全不同的概念搞混。原因有可能是两个概念发音比较接近,或者字形相似,亦或者没有什么相似点,而只是在大脑中奇怪的产生了关联而已,就像量子作用。
回到开头,说说我的这个问题。有一段时间,对于Linux中将wait_queue加入等待队列头,然后再等待被唤醒的逻辑总是想不清楚,直到后来有一天突然搞明白,我是将等待队列和工作队列搞混了。
下面,我们简单总结下Linux中的三个重要队列。
第一个是运行队列。run_queue是内核中极其重要的一个队列。任务调度就是基于这个队列实现的。每当一个进程可以运行的时候(无论是资源满足还是新创建),内核调度器schedule就将其放入运行队列中,当其被选中的时候,通过swich_to调用,完成进程切换,然后执行。
所谓切换,就是将当前进程的上下文保存起来,然后选一个新的进程,加载其上下文。当CPU的PC寄存器切换为被调度进程上次调出时保存的下一条指令后,被调度进程就获得了CPU的使用权,开始执行。
第二个就是等待队列。wait_queue也是内核中非常重要的一个队列。内核的睡眠机制就是基于等待队列实现的。首先需要了解,睡眠只存在于非原子上下文,基本就是内核线程或者进程上下文。一般的工作流程是这样的,进程会将自己加入一个等待队列,然后调用schedule进行调度。调度接口中,会将当前进程从运行队列移除。当进程运行的条件重新满足后,一般是资源满足的地方(还有一种是信号),会唤醒等待队列上的进程。主要的操作就是将进程从等待队列移除,然后重新加入运行队列。这样当调度再次发生后,进程就又有机会重新被调度到并执行了。
第三个是工作队列。work_queue在内核中有多个变种,但基本逻辑都差不多。内核工作线程检查工作队列,执行队列上注册的回调函数。工作队列运行在线程上下文,这在内核的有些地方可能是非常重要和有用的,比如DMA的一些内存释放是需要在线程上下文执行的,这样在中断中释放时,就需要将释放工作添加到工作队列上,让其切入线程上下文执行。