进程调度就是让进程从一种状态切换到另外一种状态。Linux中进程的主要状态以下,html
值 | 状态 | 缩写 | 含义 |
---|---|---|---|
0 | TASK_RUNNING | R | 正在运行或可运行 |
1 | TASK_INTERRUPTIBLE | S | 可中断的休眠 |
2 | TASK_UNINTERRUPTIBLE | D | 不可中断的休眠 |
4 | __TASK_STOPPED | T | 中止状态,当进程接收到SIGSTOP等signal信息 |
8 | __TASK_TRACED | t | 跟踪状态,进程被debugger程序暂停,好比使用ptrace()调试 |
16 | EXIT_ZOMBIE | Z | 僵尸状态,进程结束时调用do_exit()先进入僵尸状体 |
32 | EXIT_DEAD | X | 死亡状态,父进程使用waitpid()或wait4()回收死亡的子进程后,状态由EXIT_ZOMBIE转换为EXIT_DEAD |
下图粗略的展现了进程状态的切换,能够更加直观的理解各类进程状态。算法
上图中须要补充说明的是,进程正在”占有CPU执行“时应该处于TASK_RUNNING状态。EXIT_ZOMBIE状态应该是很短暂的,父进程须要使用wait类系统调用回收子进程,回收后进程状态就转变为EXIT_DEAD,若是父进程一直不回收,子进程就变为僵尸进程。__TASK_TRACED状态只有在使用debugger是才会出现,例如使用gdb设置断点,进程在断点上暂停时就处于跟踪状态。缓存
系统中包含许多进程,内核有责任让全部进程都获得运行,并让重要的进程得到更多的运行。这就须要一种机制管理全部的进程,在内核中安排进程执行的模块被称为调度器(scheduler)。数据结构
因此,调度器将解决两个核心的问题:分配合适的时间片和合理安排进程执行顺序。最原始的调度策略是按照优先级排列好进程,等到一个进程运行完了再运行优先级较低的一个,但这种策略彻底没法发挥多任务系统的优点。所以,随着时间推移,操做系统的调度器也屡次进化。app
Linux 2.4内核推出了O(n)调度器,O(n)调度器把时间分红大量的微小时间片(Epoch)。在每一个时间片开始的时候,调度器会检查全部处在就绪状态的进程。调度器计算每一个进程的优先级,而后选择优先级最高的进程来执行。一旦被调度器切换到执行,进程能够不被打扰地用尽这个时间片。若是进程没有用尽时间片,那么该时间片的剩余时间会增长到下一个时间片中。O(n)调度器在每次使用时间片前都要检查全部就绪进程的优先级。这个检查时间和进程中进程数目n成正比,这也正是该调度器复杂度为O(n)的缘由。当计算机中有大量进程在运行时,这个调度器的性能将会被大大下降。函数
为了解决O(n)调度器的性能问题,O(1)调度器被发明了出来,并从Linux 2.6内核开始使用。O(1)调度器的创新之处在于,它会把进程按照优先级排好,放入特定的数据结构中。在选择下一个要执行的进程时,调度器不用遍历进程,就能够直接选择优先级最高的进程。O(1)调度器会用两个队列来存放进程。一个队列称为活跃队列,用于存储那些待分配时间片的进程。另外一个队列称为过时队列,用于存储那些已经享用过期间片的进程。O(1)调度器把时间片从活跃队列中调出一个进程。这个进程用尽时间片,就会转移到过时队列。当活跃队列的全部进程都被执行事后,调度器就会把活跃队列和过时队列对调,用一样的方式继续执行这些进程。性能
Linux 2.6.23版本起,彻底公平调度器(CFS,Completely Fair Scheduler)取代了O(1)调度器。CFS调度器不对进程进行任何形式的估计和猜想。这一点和O(1)区分互动和非互动进程的作法彻底不一样。CFS调度器增长了一个虚拟运行时(virtual runtime)的概念。每次一个进程在CPU中被执行了一段时间,就会增长它虚拟运行时的记录。在每次选择要执行的进程时,不是选择优先级最高的进程,而是选择虚拟运行时最少的进程。彻底公平调度器用一种叫红黑树的数据结构取代了O(1)调度器的140个队列。红黑树能够高效地找到虚拟运行最小的进程。CFS调度器会根据进程的优先级来计算一个时间片因子。一样是增长250纳秒的虚拟运行时,优先级低的进程实际得到的可能只有200纳秒,而优先级高的进程实际得到可能有300纳秒。这样,优先级高的进程就得到了更多的计算资源。spa
进程优先级影响调度器的时间片分配和进程执行顺序。Linux根据进程特性,在优先级上把进程分为两大类:实时进程和普通进程。操作系统
实时进程也并非真正的实时,一样须要通过进程调度,只是会先级于普通进程运行。进程的优先级是一个0到139的整数。数字越小,优先级越高。其中,优先级0到99留给实时进程,100到139留给普通进程。普通进程的默认优先级时120,能够经过nice命令来修改进程的默认优先级。下面的命令表示将默认优先级改成(120-20)。线程
$nice -n -20 ./app
普通进程的默认优先级称为静态优先级,进程运行时实际采用的是动态优先级。调度程序经过增长或减小进程静态优先级的值来奖励IO消耗型进程或惩罚cpu消耗型进程,调整后的优先级称为动态优先级。动态优先级的计算公式以下。
动态优先级 = max(100 , min(静态优先级 – bonus + 5 ,139))
bonus是范围0~10的值,值小于5表示下降动态优先级以示惩罚,值大于5表示增长动态优先级以示奖赏。
Linux系统中,实时进程和普通进程采起了不一样的调度策略。实时进程使用是实时调度策略,有三种:SCHED_FIFO,SCHED_RR,SCHED_DEADLINE。
实时进程使用0~99的优先级,就绪进程使用队列的方式组织。相同优先级的实时进程都保存在一个列表中,再根据优先级排列起来。调度器老是先选取优先级最高的进程来运行。实时进程在下列状况下可让出CPU,
普通进程的调度策略有三种,由CFS调度器实现,分别是:SCHED_NORMAL,SCHED_BATCH,SCHED_IDLE。
Linux进程调度的介绍就简单说这些,主要是概念普及,实用为主。下面简单列举一下调度相关的函数。
Affinity表示CPU的亲和性,就是让进程在指定的CPU上尽可能长时间地运行而不被迁移到其余处理器,也称为CPU关联性。再简单的点的描述就将指定的进程或线程绑定到相应的CPU上。在多核运行的机器上,每一个CPU自己本身会有缓存,缓存着进程使用的信息。若是进程被调度到其余CPU上,cache命中率就会下降。当绑定CPU后,程序就会一直在指定的CPU上运行,不会被调度到其余CPU上,能够提升性能。
参考文章: