1. 进程优先级
当多个进程等待访问资源,需要排队,所以优先级可以确定访问资源的先后顺序。有优先级的原因就是资源狼多肉少。
在linux中优先级的本质也就是task_struct中一个整型字段(PRI),默认值是80,并且范围是[60,99],规则是数字越小优先级越高。
linux优先级允许被用户手动调整,但改变的不是PRI这个字段的值,这个是不允许改变的,改变的是task_struct中另一个整型字段(nice)。最后该进程的优先级通过这两个字段相加确定。注意nice的范围是[-20,19],刚好默认的PRI的值加上nice就得到了[60,99]的范围区间。
限制优先级范围的原因是规避进程饥饿问题
2. 进程的调度和切换
一些概念:
- 当一个进程在运行时,CPU不是必须要将这个进程的代码执行完,因为如果这样,遇到一个死循环代码,那么其他的进程就会崩溃。所以现在的操作系统都是基于时间片轮转执行的,当一个进程的时间片到了,CPU就会执行下一个进程。
- 进程的竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
- 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰,一个进程崩溃不会影响其他进程。
- 并行:多个进程在多个CUP下分别同时进行。
- 并发:多个进程在一个CUP中基于时间片的轮转执行,切换进程的方式,在一个时间段内,多个进程都得以推进。
切换:
当一个程序被CPU运行时,会产生大量的临时数据存放在CPU的寄存其中,当该进程的时间片到了之后,为了下次在运行的时候知道从哪开始运行和运行需要的数据,所以会将寄存器中的内容(也叫一个进程的硬件上下文)拷贝到task_struct中的一个区域,再第二次被调度运行时,又将里面的数据覆盖到CPU中的寄存器中来恢复上下文继续运行该程序。
调度:
我们先来看一下cup的运行队列
里面的queue[140],就是所需要维护的进程,其中0~99目前我们不关心,我们来看100-139,大家有没有发现个数刚好和优先级的个数[60,99]相等,没错,他们就是一一对应的,所以遍历这个数组就天然的先执行优先级高的进程。
为了提高遍历的效率就使用了int bitmap[5],一共32 * 5 = 160位,使用140位,刚好和队列对应,一位对应一个队列,1表示有,0表示没有。大大提高了效率。
这很简单的解决了优先级问题,但这种模式会容易造成进程饥饿,所以linux提供了两个这样的队列数组,一个叫活跃队列,一个叫过期队列。有一个指针指向活跃队列,一个指针指向过期队列。活跃队列就是CUP正在执行的进程,当该进程时间片到了就会按照优先级进入到过期队列等待第二次调度,在活跃队列执行的过程中,如果有新的进程加入,也是会按优先级进入过期队列。当活跃队列中没有了进程,我们只需要交换指针,原来的活跃队列就变成了过期队列,过期队列就变成了活跃队列,如此往返,效率仅仅为O(1)。