管理CPU
我们通过多进程图像来管理CPU,所以说多进程图像是核心部分。
那么,操作系统如何管理CPU呢?
总而言之,就是不断的自动取址执行!
一旦给出了第一个地址,后面的地址是自动累加的。
CPU使用率
我们在程序中执行计算以及一些IO操作。
前者在电子硬件上完成,后者要涉及到磁盘机械运动。
一次IO的时间和一次计算的时间比是:10^6 : 1,可见IO操作十分耗时。
所以对于CPU而言,它执行一个程序时利用率非常低,因为它绝大多数时间都在等待IO操作。而且我们也不能跳过IO语句,因为它可能和接下来的计算相关。
在这种情况下,CPU的利用率接近于0。
提高使用率方法
解决这个问题,可以在等待的时候切出去计算别的程序,在一定时候再回来。
简言之,就是:
在切换的时候,我们 修改寄存器PC就行了吗?
事实上,我们还需要记录返回的地址,要记录ax(记录切出去时的状态),也就是“保护现场”,保证CPU以后还能切回来。
每个程序有了一个存放信息的结构:PCB
进程
CPU切换的这些程序(运行程序),我们称为进程。
相比起静态程序,它的特点是:
多进程图像
操作系统记录进程,并按合理次序分配资源、进行调度,这就是多进程图像。
为每个进程创建一个结构体:PCB,记录进程的信息。
从启动开始到关机结束都在执行。
具体过程
fork()创建第一个进程,init执行shell
进程shell再启动其它进程
int main(int argc, char* argv[])
{
while(1){
scanf(“%s”,cmd);
if(!fork()){
exec(cmd);
}
wait();
}
}
多进程的组织(PCB,状态,队列)
一个进程:执行(单一PCB)
一些进程:等待执行(就绪队列)
一些进程:等待某事件(比如,磁盘等待队列,在此队列中的,即使在就绪队列中排到了头,也无法执行,因为它的前置条件缺失了)
使用PCB结构体来组成一些数据结构。
进程状态图:
用状态推进多个进程。
多进程的交替
比较复杂,在这里以一个实例介绍。
一个进程启动了磁盘读写,它把自己的状态变成阻塞态,操作系统将这个进程放到磁盘等待队列中,再进行交替。
交替的过程:
先从就绪队列中找到下一个进程(选择合适的进程是很重要的),让下一进程成为当前活跃的进程。
总之,就是:
进程调度:FIFO , 或者设置优先级。
切出去的时候,把物理CPU的一些重要信息保存到一个PCB中,再把接下来要执行的进程的PCB存到物理CPU中。(需要用汇编代码书写)
多进程之间的影响
多个进程交替执行,它们都需要被放到内存中。只有这样CPU才能取址执行。
但是,多个进程放在一起,会出现问题:其中一个进程访问的地址,很有可能是另外一个进程使用的地址,这种访问是破坏性的。
为了避免这一现象,需要限制某些地址的读写,也就是多进程的地址空间分离。
基本思想是通过映射表来实现这种分离。
地址并非真实的地址,而是对应着一个映射,每个进程都有着自己的一个映射表。两个进程虽然访问同一个“地址”,但是由于映射规则不一样,它们实际*问了不同的地址。这样就保证了多个进程在内存*存。
多进程的合作
举例:打印进程。
多个文档需要打印的话,先将打印内容放到打印队列,然后再去做别的事情。而打印这边需要从队列中取出内容。
一个存放内容,一个取出内容,两者之间形成合作关系。
为了保证合作过程能够有序进行,需要对它们做一些处理,比如组织多个文档按照怎样的顺序进入队列。
#define BUFFER_SIZE 10
typedef struct { ....} item;//共享数据
item buffer[BUFFER_SIZE];
int in = out = count = 0;
生产:
while(true) {
while( count == BUFFER_SIZE ) ;//如果已满 在这里死循环
buffer[in] = item; //不满的话直接放入
in = (in + 1) % BUFFER_SIZE
count++;
}
消费:
while(true){
while(count == 0);
item = buffer[out]; //取出内容
out = (out + 1)%BUFFER_SIZE;
count --;
多个进程都需要修改count。如果切出的时候未能正确修改count的值(还没来得及修改就被切出),就会发生错误。
所以在修改count的时候,需要将其上锁。也就是说,在一个进程还没修改完count前,不能切换到另一个进程。