25 | 多个活动要安排(上):多进程如何调度?
LMOS
该思维导图由 AI 生成,仅供参考
你好,我是 LMOS。
上节课,我们了解了什么是进程,还一起写好了建立进程的代码。不知道你想过没有,如果在系统中只有一个进程,那我们提出进程相关的概念和实现与进程有关的功能,是不是就失去了意义呢?
显然,提出进程的目的之一,就是为了实现多个进程,使系统能运行多个应用程序。今天我们就在单进程的基础上扩展多进程,并在进程与进程之间进行调度。
“你存在,我深深的脑海里,我的梦里,我的心里,我的代码里”,我经常一边哼着歌,一边写着代码,这就是我们大脑中最典型“多进程”场景。
再来举一个例子:你在 Windows 上,边听音乐,边浏览网页,还能回复微信消息。Windows 之所以能同时运行多个应用程序,就是因为 Windows 内核支持多进程机制,这就是最典型的多进程场景了。
为什么需要多进程调度
我们先来搞清楚多进程调度的原因是什么,我来归纳一下。
第一,CPU 同一时刻只能运行一个进程,而 CPU 个数总是比进程个数少,这就需要让多进程共用一个 CPU,每个进程在这个 CPU 上运行一段时间。
第二点原因,当一个进程不能获取某种资源,导致它不能继续运行时,就应该让出 CPU。当然你也可以把第一点中的 CPU 时间,也归纳为一种资源,这样就合并为一点:进程拿不到资源就要让出 CPU。我来为你画幅图就明白了,如下所示。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了多进程调度的原因和管理进程的方法。首先,文章解释了为什么需要多进程调度,主要是因为CPU同一时刻只能运行一个进程,而系统中通常有多个进程需要共享CPU资源。其次,文章详细介绍了管理进程的方法,包括进程的生命周期、进程状态的定义和进程的组织方式。文章还展示了如何初始化管理进程的数据结构,并设计实现了进程调度器。通过对进程调度器的设计和实现,读者可以了解如何在合适的时间点进行进程调度,从而让系统能够高效地运行多个应用程序。文章内容涵盖了进程调度的原理和实际操作,对于想深入了解多进程调度的读者具有一定的参考价值。文章中还介绍了进程调度器的入口函数、获取当前运行的进程、选择下一个进程以及获取空转进程的方法,为读者提供了实用的技术指导。整体而言,本文内容深入浅出,适合对多进程调度感兴趣的读者阅读学习。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》,新⼈⾸单¥68
《操作系统实战 45 讲》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(22)
- 最新
- 精选
- Ziggy_aa置顶一刷的时候只是简单过了内容,到后面就跟不上了。现在二刷是跟着源码一步一步在看。从一开始初始化部分需要对照学习。渐渐熟悉代码风格和逻辑后,从内存中段部分开始就变成了先自己理解代码,再看文章确认了。还是推荐大家把源码读起来,学习的过程中会感觉越来越快。 这一章节最后的几个切换函数让我苦恼了一下。后来发现是自己搞蒙了两件事: 1. 如果你也一下子没反应过来为什么寄存器保存和复原的内容里没有 CS 和 RIP。那是因为 call 指令调用的时候自动已经把它们入栈了。别把这个搞忘了。 2. 我们是在调用 __to_new_context() 之前就已经切换到了新进程的栈了。这样我们在调用 __to_new_context() 的时候,已经是入栈在新进程的栈,所以,随着我们 return 回去,我们就在运行新的教程了。 至于新建进程为什么要用 retnfrom_first_sched 调转。对比代码可以发现,新建进程的 stack 和 __to_new_context() 返回后所需要的 stack 内容是不同的 (新建进程里还有 segment registers 的内容)。
作者回复: 对的
2022-07-224 - neohope置顶一、数据结构 全局有一个osschedcls变量,其数据结构为schedclass_t,用于管理所有cpu的所有进程。 schedclass_t包括一个 schdata_t数组,每个cpu对应一个。schedclass_t[i],用于管理第i个cpu的全部进程。 schedclass_t[i]包括一个thrdlst_t数组,每个进程优先级对应一个。schedclass_t[i].schdata_t[j]中,管理了第i个cpu的,优先级为j的全部进程。 二、初始化 进程结构初始化: init_krl->init_krlsched->schedclass_t_init ->对于每个cpu,调用schdata_t_init,进行初始化 ->对于每个cpu的每个进程优先级,调用thrdlst_t_init初始化列表 idel进程初始化: init_krl->init_krlcpuidle ->new_cpuidle->new_cpuidle_thread->krlthread_kernstack_init【krlcpuidle_main传参】->krlschedul ->krlcpuidle_start->retnfrom_first_sched启动idel进程 三、进程调度 init_krl->init_krlcpuidle->new_cpuidle->new_cpuidle_thread->krlthread_kernstack_init【krlcpuidle_main传参】->krlschedul ->krlsched_retn_currthread根据当前cpuid,获取正在运行的进程 ->krlsched_select_thread,获取下一个运行的进程 A、根据当前cpuid,选择优先级最高进程列表中的第一个进程 B、并将该优先级的当前进程,重新放回进程列表 C、如果没有进程要运行,则返回idel进程 ->save_to_new_context,进行进程切换 A、保存当前进程的寄存器状态,到当前寄存器的栈 B、切换栈寄存器,指“向下一进程”的栈 C、调用__to_new_context D、从“下一进程”的栈,恢复寄存器状态 E、而上面这些保存的状态,都是“下一进程”在释放CPU时保存好的 F、当CPU再次回到这个所谓的“下一进程”时,又会用同样的方式还原现场,继续执行 ->->__to_new_context A、更新当前运行进程为“下一个进程” B、设置“下一进程”的tss为当前CPU的tss C、设置“下一进程”的内核栈 D、装载“下一进程“的MMU页表 E、如果“下一个进程”没有运行过,调用retnfrom_first_sched,进行第一次运行初始化 ->->-> retnfrom_first_sched 设置好新进程的相关寄存器和栈,直接运行新建进程的相关代码
作者回复: 大佬 梳理总结到位
2021-07-06314 - pedro置顶文中已有答案: retnfrom_first_sched 函数不会返回到调用它的 __to_new_context 函数中,而是直接运行新建进程的相关代码。 新的进程暂时还没有上下文,所以也就没办法切换了,于是直接运行。
作者回复: 对的 铁子
2021-07-055 - 菜鸟我想问一下进程产生的整个过程是怎样的?即:命令行运行程序实例,是怎么初始化进程?是怎么把磁盘上的程序加载到内存?怎么确定进程的入口地址等
作者回复: 看看后面的课程
2021-08-101 - 青玉白露文中——“retnfrom_first_sched 函数不会返回到调用它的 __to_new_context 函数中,而是直接运行新建进程的相关代码”——就是思考题的答案了。 retnfrom_first_shed可以视为一个初始化的过程
作者回复: 哈哈 正确的
2021-07-061 - cugphoenix新建进程初始化内核栈的时候,会给存储在内核栈中的intstkregs_t.r_rip_old,r_cs_old,r_rflgs...这些赋值,在retnfrom_first_sched里面iretq时,正好会把这些值弹出到对应寄存器中,从而实现新进程的运行。
作者回复: 正确的
2021-07-061 - │.Sk老师好,请问 td_context.ctx_nextrip 这个属性是不是其实没有用到呀?
作者回复: 是的 x86上没用到
2022-09-03归属地:湖北 - likejjj除了进程自己让出cpu,进程调度是不是都是时钟中断触发的?时间片粒度的大小,是不是由时钟的频率决定的?比如1/250
作者回复: 那不一定
2022-06-29 - 如果是你请问又是谁调用的进程调度器呢?
作者回复: 中断 内核
2022-06-13 - 艾恩凝内存一过,一马平川
作者回复: 内存很难
2022-05-05
收起评论