多进程图像
操作系统记录进程,并按照合理的次序交替推进(分配资源,不断调度),提高CPU利用率和程序执行速度,这就是操作系统的多进程图像。
当操作系统启动时,多进程图像就出现了。 在linux内核源码main.c文件中,对操作系统初始化时,通过fork()
系统调用启动了1号进程,进行初始化,再启动shell。 在shell中,我们输入命令时,会再调用fork()
启动其他进程。
// main.c
if (!fork()) {init();
}// shell
while(1) {scanf("%s", cmd);if (!fork()) {exce(cmd);}wait();
}
在windows中我们可以通过任务管理器看到所有进程,Linux可以通过输入top
或ps
命令查看进程,这就实现了进程的可视化。
组织调度交替执行进程
进程队列
操作系统会将进程划分成不同的队列,并用PCB来记录进程信息。
比如有的进程正在执行的队列,有的进程在等待被执行的队列,有的进程在等待硬盘读取结果的队列,还有的进程在已经执行结束的队列。
其实跟我们在食堂排队一样,一条队伍排队等着打饭,一条队伍排队等着打菜,一条队伍排队等着打汤。
进程状态
操作系统通过进程的状态来划分不同的队列。
新建态,刚刚被创建,分配资源。
就绪态,进程已经可以被执行了,等待CPU中。
运行态,进程正在被CPU执行。
阻塞态,进程正在等待,磁盘等IO操作完成返回结果。
终止态,进程执行结束。
操作系统对通过状态的不同管理进程,将相同状态的进程连接成一个队列,通过调度算法不断交替执行进程,改变进程状态,从而推进多个进程不断向前推进。
进程的管理和我们现实生活中处理方式一样,比如我们去政府大厅办事,不同窗口的工作人员就是CPU。 在机器正在扫码领取排队号的人就处于新建态,正在办理业务的人就处于运行态,坐在椅子上等的人就处于就绪态,而办理业务的时候发现身份证没带,去一边等朋友送身份证的人就是阻塞态,办理完了的人就处于终止态了。
调度算法
进程数量远大于CPU数量,操作系统就要对进程进行调度,如果调度不合理也会导致效率低的问题,极端情况下CPU无事可干,再比如有的进程比较重要需要优先执行完,操作系统还需要将CPU资源优先分配给这类进程。
总之一个好的调度算法非常重要,这也是很多计算机科学家在不断研究推进的一个课题。
最简单的调度算法就是FIFO+优先级。 FIFO就是先进先出算法,高优先级的先被执行,相同优先级下,先进入队列的先被执行,后进入队列的后执行。
其他调度算法有兴趣可以慢慢研究。
进程切换
进程切换首先修改PCB中的状态值,并插入到对应的状态队列中。
调用schedule()
模块进行切换,getNext()
可以理解成内部实现了一个调度算法,从就绪队列中取出下一个可被执行的进程。
PCB信息切换,switch_to()
就先保存被切换的进程信息,再初始化或恢复即将执行的进程信息。
代码只是简单描述,实际工作非常复杂。
其实对照我们上面的描述,先将资源,比如寄存器中的值保存到当前进程的PCB中。然后将即将执行的进程信息从PCB中恢复或初始化,比如PCB中保存的寄存器值赋值给寄存器。
多进程的内存隔离
多进程执行过程可能遇到下图这个问题。
当进程1使用数据的内存地址是进程2代码的指令地址。 当进程1执行的过程中,有可能在0x100
地址写入新值,导致进程2被破坏无法执行。
这个问题解决的办法就是将不同进程使用的内存进行空间分离。
因此需要有个中间带进行隔离,在进程和物理内存之间引入映射表。 进程使用的内存地址,通过映射表转换成物理内存地址,从而使不同进程的内存地址进行分离。
如下图所示,虽然进程1和进程2都使用了内存地址0x100
,但是通过映射表获取的物理地址分别是0x780
和0x1260
。
操作系统为每个进程的设置的映射表,是按照一定映射规则来的,具体细节不去研究,但是可以保证内存地址空间分离,每个进程都在自己的一亩三分地里面执行。
就好比南京和福州都有鼓楼区,虽然名字一样,但是南京和福州空间已经分离了,所以各地的人说到鼓楼区并不会冲突。
多进程合作
进程也可以相互合作,但是如果两个进程共同修改使用相同的数据,可能会导致数据错乱的问题。
比如一个传统模型-生产者消费者模型。
生产者进程负责生成数据,消费者进程负责使用数据。
如下图所示,生产者进程,生产一个数据count++
,当count=10
时,停止生产数据。消费者进程,消费一个数据count--
,当count=0
,停止消费数据。
消费者进程和生产者进程共享了count
。
由于进程是交替执行,有可能生产者进程生产了数据,但是count
还没有+1,就切换到了消费者进程,导致消费者进程使用的count
的值是错误的,从而导致合作结果产生错误。
如果要解决这个问题,就需要保证当一个进程正在使用count
时,其他进程不会使用。 其他进程只有等到count
被释放时,才能使用。
这就是多进程的锁机制,实现进程同步,后面会详细说。
总结
操作系统通过PCB保存进程信息。
操作系统根据进程的执行情况设置不同的状态,并划分到相应的状态队列中,通过调度算法交替切换执行进程。
每个进程通过映射表访问物理内存地址,实现了不同进程之间的空间隔离。
多进程合作共享数据会可能导致数据不一致等问题,通过锁机制实现进程同步,合理推进进程顺序。
PS: 以上图片来自MOOC的哈工大李老师的操作系统课程,李老师的课讲的非常好,推荐大家观看学习,如果文章有错误之处,欢迎指正,目前还处于学习阶段。