Linux进程优先级与调度解析

Linux进程优先级与调度解析
文章目录前言优先级概念PRI与NI相关概念竞争性独立性并行并发进程切换调度算法Linux内核里面的链表结构调度策略active指针和expired指针前言之前我们简单介绍了Linux中系统的几种状态这篇文章我们来对进程的优先级、进程调度做简要的介绍。优先级概念之前我们了解的权限解决的是能不能的问题而优先级解决的的是在能的基础上先后的问题。优先级是进程在已经能得到某种资源的前提下决定进程获得某种资源的先后顺序。为什么要有优先级呢这是因为操作系统的资源是有限的当资源不足时需要合理分配资源此时就需要设置优先级。PRI与NI查看进程优先级可使用指令**“ps -l”**如下所示其中有几个重要信息如下UID代表执行者的身份PRI代表这个进场可被执行的优先级即父进程的代号NI代表这个进程的nice值其中PRI与NI为进程优先级的相关信息其实它们就是task_struct两个整型变量。PRI是进程的优先级也就是进程被CPU执行的先后顺序PRI越小的进程优先级越高。NI是nice值其表示进程可被执行的优先级的修正数值PRI越小越快被执行当加入nice值后将会使得PRI变为PRI(new) PRI(old) nice其中PRI(old)默认为80。修改进程优先级可以先输入top指令然后输入r输入要改变优先级的进程pid再根据提示输入对应数据这里输入的为10如下所示所以更改优先级其实改的是进程的NI值。nice的取值范围为[-20 , 19]那么优先级的取值范围为[60 , 99]当nice值为负值的时候该程序会将优先级值变小即优先级会变高则其越快被执行设置的nice值必须在规定范围内若超过规定范围会取区间端点的值。但是不能频率过高地更改优先级否则会被操作系统阻拦。从上可以看出进程的优先级是有限的这是由于Linux系统为分时操作系统系统会为每个进程分配时间片同时会以相对公平公正的调度策略较为均衡地让不同的进程都能在一段时间内都能得到CPU资源所以不能过度改变优先级否则会长时间使某一进程长时间占用CPU因此要将进程的优先级控制在一定范围内。相关概念竞争性系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级。独立性多进程运行需要独享各种资源多进程运行期间互不干扰。这是因为每一个进程都有独立的内核数据结构和进程自己的代码和数据这是进程具有独立性的原因。并行多个进程在多个CPU下分别同进进行运行这称之为并行。并发多个进程在⼀个CPU下采用进程切换的方式在⼀段时间之内让多个进程都得以推进称之为并发。进程切换进程切换其实际含义是任务切换,或者CPU寄存器切换。当多任务内核决定运行另外的任务时,它保存正在运行的进程的临时数据也就是上下文数据并开始下一个任务的运行。如下所示寄存器是共享的但是寄存器里面的数据本质是进程私有的。调度算法Linux内核里面的链表结构在结构体内部知道对象某一成员的地址后就可以算出该成员地址相对于起始地址的偏移量计算方式如下offsetof(结构体类型, 成员名) (结构体类型)(((结构体类型 *)0)-成员名)知道偏移量后进一步可以算出结构体对象的地址结构体地址 (结构体类型*)((char*)成员地址 - offsetof(结构体类型, 成员名))在Linux系统中链表结构如下进程是通过链表组织起来的只不过与之前链表不同的是这里的链表中的下一个指针不指向下一个结构体的首地址而是指向下一个结构体中的Node成员的首地址得到结构体内其中一个变量的起始地址就可以获得结构体的地址以及其它变量的地址。Linux系统这样设计链表结构是为了增加对链式管理的扩展性。当要对新的设备进行管理时可以在结构体内相关设备的属性使得代码只需要维护一份。在task_struct中会维护一个成员使得该进程可以放在全局的双链表中也会维护另外一个成员可以使得该进程在运行队列中。因此一个进程既可以属于全局的也可以属于某一队列的。调度策略Linux系统中每一个CPU都有一个对应的运行队列如下所示其中该队列有一个成员变量queue[140]其中0-99对应的位置为实时优先级这里不做讨论100-139对应的位置为普通优先级而优先级的范围为[60, 99]系统会将进程优先级映射到[100, 139]区间因此优先级数字本质是数组下标数组中的每一个都会存储一个先进先出的队列队列中存储着对应优先级的进程task_struct选择进程时先根据优先级选择对应的队列然后再根据先进先出的规则对优先级进行调度。因此根据优先级选择进程的时候本质是一个hash计算的过程一旦确定队列剩下的就是根据FIFO规则进行调度。先选择一个不为空的队列再选择一个最先到达的进程。bitmap[5]存储的时对应的位图该成员用来表示某一个队列是否为空。nr_active用来表示当前队列中进程的个数若大于0再查bitmap找到不为空的队列。active指针和expired指针若持续有优先级较高的进程到达低优先级的进程就会得不到调度这种现象称为饥饿现象。在Linux系统中为了解决该问题系统内定义了一个数组 struct prio_array_t array[2];其中struct prio_array_t结构如下structprio_array_t{nr_active,bitmap[5],queue[140]};数组中两个元素对应不同的哈希表同时有两个指针active指针和expired指针分别指向活跃哈希表(也称活跃140队列)和过期哈希表(也称过期140队列)CPU选择进程时只会查看活跃队列里的进程当进程的时间片内后将进程放入过期队列中因此活跃队列中的进程会越来越少当活跃队列中的进程数量为0时只需要swap(active, expired)即交换两个指针按照上面的过程继续调度即可。当新进程来临时将对应进程放入过期队列。当正在调度进程优先级发生变化时先按照原来的优先级进行调度进程时间片结束后再通过nice值修改优先级再放入过期队列中即可。通过以上措施即可解决操作系统中的饥饿问题。