进程调度的原因:
一是CPU同一时刻只能运行一个进程,并且CPU 的个数总是少于进程个数,就需要让进程共用一个CPU;二是当一个进程拿不到资源时就应该让出CPU,否则会一直霸占CPU;
1、管理进程:
进程的生命周期:
进程的状态:运行、睡眠、等待、新建、僵死;
组织进程:
设计思想:链表数据结构,一个优先级对应一个链表头;
schedclass_t 结构是一个总的全局结构,其中包含一个schdata_t结构数组,大小根据CPU数量决定,每个schdata_t 结构,包含一个进程优先级大小的thrdlst_t 结构数组,挂载进程;
管理进程初始化:
init_krlsched 函数:初始化schedclass_t 结构实例,调用schedclass_t_init 函数,逐级初始化上述 的三个结构数组;
init_krlsched 函数被内核功能层的入口函数init_krl 函数调用;
2、实现进程调度器:
停止运行当前进程,让CPU开始执行另一个进程;
进程调度器入口:
krlschedul接口函数:调用krlsched_retn_currthread 函数返回当前运行进程,接着调用 krlsched_select_thread 函数选择下一个运行的进程;最后调用save_to_new_context 函数从当前进程切换到下一个进程;
获取当前运行进程:
krlsched_retn_currthread 函数 ,先得到是哪个CPU的cpuid,借此通过获取数据结构中sda_currtd 成员得到当前运行的进程;
选择下一个进程:
这是进程调度算法的核心:关乎进程的吞吐量、能否及时响应请求、CPU的利用率、各个进程之间运行获取资源的公平性;
cosmos系统选择简单的一个调度算法:优先级高的作为下一个进程;
krlsched_select_thread 函数:从最高优先级开始扫描,若当前优先级的 链表不为空,取出该链表下的第一个进程,执行脱链,旧的当前进程先挂入链表尾,再将新的当前进程挂入tdl_curruntd 字段;
若扫描到最后,也没有找到进程,就返回默认的空转进程;
获取空转进程:
空转进程的必要性:调度器的功能必须是从一个切换到另一个进程,若没有下一个进程,且上一进程又不能运行,系统将停止运行;
krlsched_retn_idlethread 函数,与获取当前进程函数设计一样,只不过获取的schdata_t 结构中的sda_cpuidle 空转进程;
进程切换:
进程在内核中的函数调用路径:通俗讲就是一个进程中的函数层层调用,直到调用进程调度器函数,中间的调用函数顺序就是函数调用路径;通过栈保存;
save_to_new_context 进程切换函数:首先保存当前进程的通用寄存器到当前进程的内核栈中;
然后 保存CPU的RSP寄存器 到当前进程的机器上下文中,并且读取 保存在下一个进程机器上下文的RSP的值,把它存到CPU的RSP寄存器中;
接着 调用函数切换MMU页表;
最后从下一个进程的内核栈中恢复下一个进程的通用寄存器;
通过切换进程的内核栈,切换进程,因为进程的函数调用路径就保存在对应的内核栈中,只要调用 krlschedul 进程调度函数,最后的函数调用路径一定会停在 save_to_new_context 函数中,当 save_to_new_context 函数一返回,就会导致回到调用 save_to_new_context 函数的下一行代码开始运行,在这里就是返回到 krlschedul 函数中,最后层层返回;
这个机制的前提是:下一个进程已经被调度过,执行过krlschedul函数;
对于新建进程,没有调用过krlschedul 函数,需要特殊处理;
__to_new_context 函数:负责设置当前运行的进程,处理CPU发生中断时需要切换栈的问题,又切换一个进程的MMU页表;如果是新建进程第一次运行,就调用retnfrom_first_sched 函数进行处理;
该函数不会返回到调用它的__to_new_context 函数,直接运行新建进程的代码;
标签:调用,函数,调度,context,进程,CPU,14 From: https://www.cnblogs.com/xuan01/p/17363728.html