1. 什么是多进程图像?
- 如何使用CPU? 让程序执行起来
- 如何充分利用CPU? 启动多个程序,交替执行
- 多进程图像:多个进程使用CPU的图像
操作系统 把这些 启动了的程序(进程)用PCB记录好,再按照合理的次序推进(分配资源、进行调度)
PCB:Process Control Block,用来记录进程信息的 数据结构
2.多进程如何组织?
有程序在执行;有一些进程在等待执行,放在就绪队列中;有一些进程在等待某个事件,放在 磁盘等待队列
PCB 用来记录进程信息的 数据结构,帮助系统 感知、管理进程
3.多进程的组织:PCB + 状态 + 队列
举例说明 进程状态:
银行窗口办理业务
运行态:正在办理业务的人
就绪态:后边等待的人
阻塞态:等待的人 特别多,超出等待队列范围,剩下的 办理业务的人 就是阻塞态
终止态:办理完业务的人
多进程组织:PCB放到不同的队列中,通过 切换进程的状态 推进进程
4.多进程如何交替?
- 一个进程启动磁盘读写;
- 状态置为阻塞态(pCur.state = ‘W’;)
- 将当前进程的PCB pCur 放到 磁盘阻塞队列(DiskWaitQueue)
- schedule() 切换进程:保护当前进程的现场,恢复下一个进程的 执行现场
schedule()
{
pNew = getNext(ReadyQueue);// getNext 根据就绪队列找到下一个进程
switch_to(pCur, pNew); // switch_to根据当前进程和下一个进程的PCB (pCur,pNew) 做切换
}
5. 进程交替的三个部分:队列操作+调度+切换
进程调度是一个很深刻的话题,如今也是 值得研究的课题
- FIFO:先进先出,公平策略,没有考虑进程执行的任务的区别
- Priority:优先级,优先级怎么设定?依靠 优先级,可能会导致进程饥饿。
切换进程时需要:
保存 正在执行的进程的 现场:将 物理CPU的该进程的信息,包存到 一个结构体 PCB中
恢复 要执行的进程的 现场:将该进程对应的PCB 恢复到 物理CPU中
该过程需要 精细控制,需要用 汇编实现
6.多进程的地址空间分离
问题:
进程1 用到了 地址100的值,进程2 会修改 地址100的值。进程1 2同时运行,会出错。
解决:
限制对地址100的读写,每个进程都有自己的映射表,进程1的地址100可能对应的物理内存地址的780
进程2的映射表的100,对应的物理地址 可能是 1260,通过映射表实现进程的隔离
多进程的地址空间分离 是 内存管理的主要内容
7.多进程如何合作?
7.1 问题1:打印的例子
打印是将要打印的内容放到打印队列,打印机从打印队列中取数据,打印。这时打印队列有6个打印数据,
进程1,2都要打印,同时往打印队列里加 数据,那么打印队列 第7个打印数据是哪个呢?
7.2 问题2:生产者 消费者例子
buffer是共享缓冲区
生产者 生产数据,往buffer里加数据 buffer[in] = item;
消费者 消耗数据,从buffer里取数据 item = buffer[out];
通过共享变量 counter 判断,生产者是否再生产,消费者是否再消耗
如果counter错了:
生产者中,buffer已经满了,counter != BUFFER_SIZE,还往buffer里写数据,就把原来的数据冲掉了
消费者中,buffer已经空了,counter != 0,还要从buffer里取数据,其实buffer已经空了
//生产者
while(true){
//counter 是一个共享的变量,当buffer满了,就不再生产,不往buffer里写数据了
while(counter == BUFFER_SIZE)
;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
counter++;
}
//消费者
while(true){
while(counter == 0)
;
item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
counter--;
}
//共享数据
#define BUFFER_SIZE 10
typedef struct{...}item;
item buffer[BUFFER_SIZE];
int in = out = counter = 0;
举例说明 counter 错了
正常情况:
共享数据 int counter = 0;
//生产者P counter++;
register = counter;
register = register + 1;
counter = register;
//消费者C counter--;
register = counter;
register = register - 1;
counter = register;
出错例子:
设 初始值 counter = 5,生产者进程 执行一半,切到 消费者进程
// 生产者进程
P.register = counter;// counter = 5
P.register = P.register + 1; // P.register = 6, counter = 5
//消费者进程
C.register = counter; // C.register = 5
C.register = C.register - 1; // C.register = 4, counter = 5
counter = P.register; // counter = 6
counter = C.register; //counter = 4
正常情况,初始值 counter = 5,生产1个,消费1个,结束时 counter = 5
上边的错误情况,counter = 4
7.3 解决:合理的推进进程顺序
操作counter之前 要看 是否已经锁上了,锁了不能操作counter;没上锁,操作counter前 要上锁,用完了 要解锁
//生产者进程
// --给counter上锁
P.register = counter;//counter = 5
P.register = P.register +1; // P.register = 6, counter = 5
//此时消费者要执行,检查锁,发现counter锁上了,不能执行消费者代码
//生产者 继续执行
counter = P.register; // counter = 6
// --给counter解锁
//消费者执行
// -- 给counter上锁
C.register = counter;
C.register = C.register - 1;
counter = C.register; // counter = 5
操作系统通过PCB来感知多进程,PCB是系统中 最重要的结果,贯穿系统始终,后续课程会继续讲解