操作系统实战(七)


一、为什么需要多进程调度?

CPU同一时刻只能运行一个进程,而CPU个数总是比进程个数少,这就需要多进程公用一个CPU,每个进程在这个CP运行一段时间。
当一个进程不能获取某种资源,导致不能继续运行,就需要让出CPU。进程拿不到资源就需要让出CPU。
在这里插入图片描述

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、管理进程

1.进程的生命周期

通常用进程的状态表示进程的生命周期,包括运行状态、睡眠状态、等待状态、新建状态、僵死状态。
在这里插入图片描述

二、如何组织进程

使用链表数据结构来组织进程,进程有优先级,所以可以设计成每个优先级对应一个链表头。
schedclass_t是个全局数据结构,这个结构中包含了一个schdata_t结构数组,数组大小根据CPU的数量决定。
在这里插入图片描述

三、管理进程的初始化

管理进程的初始化,就是对schedclass_t结构的变量的初始化。需要定义一个schedclass_t结构的全局变量osschedcls。然后进行osschedcls初始化。
由init_krlsched函数调用schedclass_t_init函数,然后schedclass_t_init函数调用schdata_t_init函数,然后schdata_t_init函数调用thrdlst_t_init函数。

四、设计实现进程调度器

1.进程调度的入口

进程调度器不过是一个函数,只是他从一个进程运行到下一个进程。
进程调度器是为了在合适的时间点,合适的代码执行路径上进行进程的调度。这个函数的功能就是,*** 确定当前正在运行的进程,然后选择下一个将要运行的进程,最后从当前运行的进程,切换到下一个将要运行的进程。***

2.如何获取当前运行的进程

获取当前运行的进程,是为了保存当前进程运行的上下文,确保在下一次调度到当前运行的进程时能够恢复。每次切换到下一个进程运行时,我们就会将下一个运行的进程设置为当前运行的进程。schdata_t结构中的sda_currtd字段保存当前正在运行进程的 地址。

3.选择下一个进程

选择进程是进程调度算法的核心,它关乎到进程的吞吐量,能否及时相应请求,CPU的利用,各个进程时间运行获取资源的公平性,这些问题综合起来就会影响整个操作系统的性能、可靠性。
本次只使用简单的优先级调度算法,就是始终选择优先级最高的进程,作为下一个运行的进程。首先,从高到低扫描优先级进程链表,然后若当前优先级进程链表不为空,就取出该链表上的第一个进程,放入thrdlst_t结构中的tdl_curruntd字段中,并把之前thrdlst_t结构的tdl_curruntd字段中的进程挂入该链表的尾部,并返回。最后,当扫描到最低优先级时也没有找到进程,就返回默认的空转进程。

4.获取空转进程

在选择下一个进程的函数中,如果没有找到合适的进程,就返回默认的空转进程。
为什么要有一个空转进程,直接返回NULL不行吗?
调度器功能必须完成一个从进程到进程的切换,如果没有下一个进程,而上一个进程又不能运行了,调度器将无处可去,整个系统也将停止运行。所以我们要设置一个空转进程,来保证系统运行。

4.进程切换

在进程切换之前,进程在内核中函数调用路径,拿什么是函数调用路径?
举个例子,比如进程 P1 调用了函数 A,接着在函数 A 中调用函数 B,然后在函数 B 中调用了函数 C,最后在函数 C 中调用了调度器函数 S,这个函数 A 到函数 S 就是进程 P1 的函数调用路径。
再比如,进程 P2 开始调用了函数 D,接着在函数 D 中调用函数 E,然后在函数 E 中又调用了函数 F,最后在函数 F 中调用了调度器函数 S,函数 D、E、F 到函数 S 就是进程 P2 的函数调用路径。
函数调用路径是通过栈来保存的,对于运行在内核空间中的进程,就是保存在对应的内核栈中。
在这里插入图片描述
首先,我们要把当前进程的通用寄存器保存到当前进程的内核栈中;然后,保存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函数中完成这个特殊处理。如果是新建进程第一次运行,就调用retnfrom_first_shed函数进行处理(这个函数不会返回调用它的_to_new_context中)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值