把握linux内核设计(九):进程调度

。由于linux提供抢占式的多任务模式,所以linux能同时并发地交互执行多个进程,而调度程序将决定哪一个进程投入运行、何时运行、以及运行多长时间。调度程序是像linux这样的多任务操作系统的基础, 只有通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的效果。当系统中可运行的进程数目比处理器的个数多,就注定在某一时刻有一些进程不能执行,这些不能执行的进程在等待执行。调度程序的基本工作就是停止一个进程的运行,再在这些等待执行的进程中选择一个来执行。

调度程序停止一个进程的运行,再选择一个另外进程的动作开始运行的动作被称作抢占(preemption)。一个进程在被抢占之前能够运行的时间是预先设置好的,这个预先设置好的时间就是进程的的时间片(timeslice)。时间片就是分配给每个可运行进程的处理器时间段,它表明进程在被抢占前所能持续运行时间。

优先级高的先运行,相同优先级的按轮转式进行调度。优先级高 的进程使用的时间片也长。调度程序总是选择时间片未用尽且优先级最高的进程运行。这句话就是说用户和系统可以通过设置进程的优先级来响应系统的调度。基于此,linux设计上一套动态优先级的调度方法。一开始,先为进程设置一个基本的优先级,然而它允许调度程序根据需要来加减优先级。linux内核提供了两组独立的优先级范围。第一种是nice值,范围从-20到19,默认为0。nice值越大优先级越小。另外nice值也用来决定分配给进程时间片的长短。linux下通过命令可以查看进程对应nice值,如下:

$ ps -elF S UID PID PPID C PRI NI ADDR SZ WCHAN TTYTIME CMD 4 S010 0 80 0 – 725 ??00:00:01 init 1 S020 0 80 0 -0 ??00:00:00 kthreadd 1 S032 0 -40 – -0 ??00:00:01 migration/0 1 S042 0 80 0 -0 ??00:00:00 ksoftirqd/0 1 S092 0 80 0 -0 ??00:00:00 ksoftirqd/1……1 S0 392 0 85 5 -0 ??00:00:00 ksmd ……1 S0 1562 0 75 -5 -0 ??00:00:00 kslowd000 1 S0 1572 0 75 -5 -0 ??00:00:00 kslowd001 …… 4 S 499 29511 0 81 1 – 6276 ??00:00:00 rtkit-daemon …… 第二种范围是实时优先级,默认范围是从0到99。任何实时的优先级都高于普通优先级。

Linux 将进程状态分为五种: TASK_RUNNING 、 TASK_INTERRUPTIBLE 、 TASK_UNINTERRUPTIBLE 、 TASK_STOPPED 和 TASK_ZOMBILE 。进程的状态随着进程的调度发生改变。

TASK_RUNNING

可运行

TASK_INTERRUPTIBLE

可中断的等待状态

TASK_UNINTERRUPTIBLE

不可中断的等待状态

TASK_STOPPED

停止状态

TASK_TRACED

被跟踪状态

TASK_RUNNING (运行):无论进程是否正在占用 CPU ,只要具备运行条件,都处于该状态。 Linux 把处于该状态的所有 PCB 组织成一个可运行队列 run_queue ,调度程序从这个队列中选择进程运行。事实上, Linux 是将就绪态和运行态合并为了一种状态。

TASK_INTERRUPTIBLE (可中断阻塞): Linux 将阻塞态划分成 TASK_INTERRUPTIBLE 、 TASK_UNINTERRUPTIBLE 、 TASK_STOPPED 三种不同的状态。处于 TASK_INTERRUPTIBLE 状态的进程在资源有效时被唤醒,也可以通过信号或定时中断唤醒。

TASK_UNINTERRUPTIBLE (不可中断阻塞):另一种阻塞状态,处于该状态的进程只有当资源有效时被唤醒,不能通过信号或定时中断唤醒。在执行ps命令时,进程状态为D且不能被杀死。

TASK_STOPPED (停止):第三种阻塞状态,处于该状态的进程只能通过其他进程的信号才能唤醒。

我们在设置这些状态的时候是可以直接用语句进行的比如:p—>state = TASK_RUNNING。同时内核也会使用set_task_state()和set_current_state()函数来进行。

linux调度器是以模块方式提供的,这样允许不同类型的进程可以有针对性地选择调度算法。完全公平调度(CFS)是针对普通进程的调度类,CFS采用的方法是对时间片分配方式进行根本性的重新设计,完全摒弃时间片而是分配给进程一个处理器使用比重。通过这种方式,CFS确保了进程调度中能有恒定的公平性,而将切换频率置于不断变动之中。

与Linux 2.6之前调度器不同,2.6版本内核的CFS没有将任务维护在链表式的运行队列中,它抛弃了active/expire数组,而是对每个CPU维护一个以时间为顺序的红黑树。该树方法能够良好运行的原因在于: * 红黑树可以始终保持平衡,这意味着树上没有路径比任何其他路径长两倍以上。 * 由于红黑树是二叉树,查找操作的时间复杂度为O(log n)。但是除了最左侧查找以外,很难执行其他查找,并且最左侧的节点指针始终被缓存。 * 对于大多数操作(插入、删除、查找等),红黑树的执行时间为O(log n),而以前的调度程序通过具有固定优先级的优先级数组使用 O(1)。O(log n) 行为具有可测量的延迟,但是对于较大的任务数无关紧要。Molnar在尝试这种树方法时,首先对这一点进行了测试。 * 红黑树可通过内部存储实现,即不需要使用外部分配即可对数据结构进行维护。 要实现平衡,CFS使用“虚拟运行时”表示某个任务的时间量。任务的虚拟运行时越小,意味着任务被允许访问服务器的时间越短,其对处理器的需求越高。 CFS还包含睡眠公平概念以便确保那些目前没有运行的任务(例如,等待 I/O)在其最终需要时获得相当份额的处理器。由于篇幅原因,这里不详细讲解CFS的实现。

没有早一步,也没有晚一步,刚好遇上了你!

把握linux内核设计(九):进程调度

相关文章:

你感兴趣的文章:

标签云: