操作系统的关键抽象:
来源《深入理解计算机》
比喻:厨师做蛋糕(现代操作系统)
做蛋糕的食谱:程序
做蛋糕的原料:输入数据
厨师:CPU
厨师阅读食谱,用原料做蛋糕的一系列动作的总和:进程
厨师的儿子跑进了,说是被蜜蜂蛰了
-厨师记录下当前做到哪一步了(保存当前进程状态)
-拿出急救手册,按其中的指示进行处理(开始另外一个进程)
把I/O设备抽象成文件,就不用对硬盘,控制台,输出设备进行操作了。
物理主存加文件就是虚拟存储器,是对内存和硬盘抽象,每一个进程都有一个超大空间。内存大小有限,不可能把所有进程都放入内存空间。每个进程有独立虚拟地址空间,会被映射到真实内存中去,程序只有一块代码被调入到真实内存中去,
进程的状态:
进入就绪态并不会占用CPU,操作系统维护一个列表,进程A在等待硬盘操作,就进入硬盘等待设备列表,等待键盘输入,就进入等待键盘列表中,等待位于CPU维护的一个列表中,不会占用CPU,就绪也不会占用。
非抢占式(dos/win3.1/win3.2)
-调度程序一旦把CPU分配给某一进程后便让他一直运行下去,直到进程完成或发生某件事件而不能运行,才将CPU分给其他进程。
-适用批处理系统,不适合交互式操作
-简单、系统开销小
-当一个进程正在执行时,系统可以基于某种策略剥夺CPU给其他进程。剥夺的原则有:
优先权原则、短进程优先原则、时间片原则
-适用于交互式系统
进程调度:评价标准
公平
-合理的分配CPU
-响应时间:从用户输入到产生反应的时间
-吞吐量:单位时间完成的任务数量
批处理系统的调度:
先来先服务
-公平,简单(FIFO队列),非抢占,不适合交互式
最短作业优先
-系统的平均等待时间最短
-但是需要预先知道每个任务的运行时间
交互式调度策略(1):轮转
每个进程分配一个固定时间片
假设进程切换一次的开销为1ms
时间片为4ms
-20%的时间浪费在切换上
时间片为100ms
-浪费只有1%,但是假设有50个进程,最后一个需要等待5s
交互式系统调度策略(2):优先级
交互式系统调度策略(3):多级队列反馈
进程间同步:
多进程情况下,共享变量counter会出错!
counter = 3;
生产者(p):
register = counter;
register = register+ 1;
counter = register;
消费者(C):
register = counter;
register = register- 1;
counter = register;
并发执行:
P.register = counter;
P.register = P.register+ 1;
生产者进程被OS打断,执行消费者进程
C.register = counter;
C.register = C.register- 1;
counter = P.register;
counter = C.register;
问题的核心
-不可控制的调度
-在机器层面,counter++,counter--并不是原子操作
临界区
-访问/修改共享资源(变量,表,文件。。。)
-当进程进入临界区时,不允许其他进程在临界区执行
解决临界区问题(1):暴力手段,关闭中断
-CPU收到时钟中断以后偶,会检查当前进程的时间片是否用完,用完则切换。
--关闭中断(时钟中断),这样CPU就不会被打断
--离开临界区一定要记住打开中断
--但是把中断操作开放给应用程序是非常危险的。
解决临界区问题(3):信号量
信号量S是个整数变量,除了初始化外,有两个操作,wait(),sinal()
或者是P/V,或者down/up
wait(S){
while(S<=0){
;//啥也不做
}
S--;
}
signal(S){
S++;
}
semaphore mutex = 1;
wait(mutex);
进入临界区
signal(mutex);
剩余区
为了确保信号量能工作,需要用一种原子的方式实现它。
解决临界区问题(4):不能忙等
typedef struct{int value;
struct process *list;
}semaphore;
wait(semaphore *s){
s->value--;
if(s->value<0){
把当前进程加到s->list中;
block();
}
}
wignal(semaphore *s){
s->value++;
if(s->value<=0){
从s->list取出一个进程p
wakeup(p);
}
}
解决临界区问题(5):用信号量解决打印问题
semaphore mutex = 1;
semaphore empty = 5;
semaphore full = 0;
生产者:
while(true)(
wait(empty);
wait(mutex);
把新产生的文件加入队列;
signal(muterx);
signal(full);
)
消费者:
while(true)(
wait(full);
wait(mutex);
把队列头的文件打印,删除;
signal(muterx);
signal(empty);
)
线程:
线程的优点:
浏览器
-线程1显示图像
-线程2从网络接收数据
文字处理器
-线程1显示图形
-线程2读取用户键盘输入
-线程3自动定时的保存文档
线程的实现:完全在用户层实现