目录
一、课程地位
二、问题
在学完整节课之后可以回头看看这些问题,自己心中会有答案。
1. EXE、ELF是什么?运行一个EXE到底发生了什么?
2. c库函数是如何实现的(printf、scanf、gets)?fread和read的区别是什么?
3. 我的破烂电脑512M内存,怎么4G的游戏也可以跑?(虚拟内存啥东西?)
4. 图中程序在运行时,CPU利用率是多少?
早期单核CPU是100%。
5. 该内存不能为written? 这是什么意思?内存坏了?“写保护” 打开了? 此应用程序还可以继续执行吗? 该错误是哪个设备第1个发现的?CPU,内存,或是其他?处理流程是什么?
6. 需要通信的程序:C/Java的应用程序数据,用matlab脚本进行分析 如何传送数据?原理是什么?有更高效的方法?
7. 浏览器:IE vs Chrome。为什么早期的IE浏览器,打开一个页面崩溃了,其他页面也跟着崩溃了? google的Chrome浏览器和IE后期版本是如何解决这个问题的?
8. 诡异的bug:我写了一个程序,测试都通过了,上线后跑了一个月运行正常,后来时不时的无法响应客户端的请求,出现的频率非常随机,但在任务管理器里却能看到这个程序。 因为难以重现,我被这个问题折磨得头发都白了,想死的心都有了。
9. 数据恢复:在磁盘上被 删除的数据,还有可能被人恢复,为什么? 操作系统本可以避免数据被恢复,但没有做,为什么?
10. 怎么样才能学好操作系统?
三、操作系统起源与发展(方法与效率)
1. 没有操作系统的时代
1946(第一台计算机诞生,那时候还没有操作系统):通过拨开关编程。麻烦!
2. 冯诺依曼计算机体系结构
程序存储,顺序执行。
思路:让计算机要做的事情编成程序放在硬盘(disk)or磁带上面,计算机运行之后将程序装载到内存里面,由CPU一条条指令从内存取出并执行,完成最终想让计算机完成的任务。
仍然没有操作系统,编程麻烦。
外设并不是直接连到总线上,而是通过控制器(例如monitor通过graphics controller)连到总线上与CPU互连。
我们与外设的交互是通过这些controller,并不是直接与外设打交道。
3. 硬件编程
controller结构如下:
当想操作打印机时,找到打印机的controller,将命令(二进制01串)写道command register。因此控制外设需要查他的控制器手册,告诉你些什么样的指令对应什么样的功能。status register对应的是外设状态,全0正常,某一位置异常那一位会返回1。
但是对于不同外设的controller编程不同,仍较为复杂。
4. 操作系统方法
(1) 对开发人员:屏蔽了底层硬件的复杂性。
原始:APP内程序通过查controller手册来控制HW(硬件)。
加入操作系统(OS软件):OS与HW交互(OS的驱动程序完成,驱动程序代码占80%-90%),APP通过C或汇编调用OS接口(比如fread等操作系统提供的一系列函数)。
因此开发人员不需要关心底层逻辑,只需调用OS接口(即系统调用system call)即可。
(2) 对小白用户:提供了良好的人机交互界面。
5. 操作系统效率
(1) 早期多人使用计算机
人工操作:将每个人的程序放到磁带上(打孔),交给计算机管理员(人工),计算机管理员将每个人的磁带分别放到计算机上运行,并将结果再交给各个人。
缺点:手动更换任务、耗时容易出错 。
(2) 改进:单道批处理系统
在这台计算机上一直跑一个监督程序,他会扫描外设上有没有任务,有任务就将他加载到内存中执行直至结束,后再扫描...
问题:例如P(I1, C1, O1, I2, C2, O2, .. )(Input -> Compute -> Output),发现C步骤非常快(≈2.6GHz,ns级),但IO(200-300ms)的速度比C慢的多得多得多。存储介质访问速度:
因此CPU利用率很低(当一个任务在执行IO操作时,CPU是空闲的)。
(3) 解决:多道批处理系统
一次载入多道程序到内存。eg:P1(I1, C1, O1), P2(I2, C2, O2)。I1I2已执行完毕,C1 -> O1+C2(一边输出一边运算)。即一个job在做IO时,切换另一个job来运行。
程序切换的条件:当前程序要做IO操作。
优点:提高了CPU利用率。缺点:任务周转时间(从提交任务至获得输出的时间)太长!例如:job1=科学计算,job2=游戏。先双击job1再双击job2,然而job1这个科学计算要执行一个月,这样你的job2游戏智能一个月之后才会启动(因为不做IO不会切换程序运行)。
(4) 解决:分时操作系统(交互式系统)
显然我们不能被动的等待程序切换,而要主动地定时地去切换程序。程序切换的时间不由程序本身的IO决定,而由时间片决定。(将CPU的时间切割成小的时间片,每个程序交互式的占用时间片执行。早期linux时间片 = 20ms,由CPU时钟中断处理程序完成程序的切换)
并发执行和并行执行:
问题1(也是多道批处理系统的问题):看下面两个程序:
int a=1; int b=3;
void main() { void main() {
a++; b--;
} }
如果不从底层机器的视角看,这两个程序并发执行没有任何问题!但:
int a=1; int b=4;
void main() { void main() {
a++; b--;
} }
1 MOV AX, [a] 4 MOV AX,[b]
2 ADD AX 5 DEC AX
3 MOV [a], AX 6 MOV [b], AX
a = 2 b = 3
本来应该按上述顺序执行(123456),但分时操作系统会主动将程序踢出切换到另一个程序,因此会出现下述情况:
int a=1; int b=4;
void main() { void main() {
a++; b--;
} }
情况1
1 MOV AX, [a] 3 MOV AX,[b]
2 ADD AX 4 DEC AX
5 MOV [a], AX 6 MOV [b], AX
a = 3 b = 3
情况2
1 MOV AX, [a] 2 MOV AX,[b]
3 ADD AX 4 DEC AX
5 MOV [a], AX 6 MOV [b], AX
a = 4 b = 4
等等
问题2(也是多道批处理系统的问题):不共用寄存器行吗?(对同一全局变量不同程序先后修改。)
int a=1;
1 void main() { 2 void main() {
a++; a--;
} }
1 MOV AX, [a] 3 MOV BX,[a]
2 ADD AX 4 DEC BX
5 MOV [a], AX 6 MOV [a], BX
原本:a = 2 a = 1
结果:a = 2 a = 0
解决:引入锁机制。
int a=1;
1 void main() { 2 void main() {
lock(a); lock(a);
a++; a--;
unlock(a); unlock(a);
} }
P1把c锁住,当时间片到了切换到P2,P2也想锁c,但c已经被P1锁了P2就锁不上了,所以卡在那不能接续执行,知道时间片切换回P1,使其运行到unlock(c),P2就可以接着运行,因此不会出现上述问题。
问题:锁机制带来的问题:死锁。执行顺序如上,1、2执行完毕后,3、4均卡住,于是P1P2陷入等待对方的死锁之中。
int a=1, b=1;
void main() { void main() {
1 lock(a); 2 lock(b);
3 lock(b); 4 lock(a);
a++; a--;
b++; b--;
unlock(b); unlock(a);
unlock(a); unlock(b);
} }
(4) 综上!逻辑梳理
单道批处理效率低 -> 提高效率:穿插运行 -> 共享寄存器问题 -> 共享全局变量问题 -> 加锁不穿插运行 -> 死锁问题。归根结底:一台计算机上跑多道程序(否则只跑一道程序的话,此门课就可以到此为止了hhh)。
6. 内核态与用户态,特权指令与非特权指令
CPU状态:内核态与用户态 -> 特权指令与非特权指令
你写的应用程序所用到的指令均为非特权指令,特权指令只有操作系统可用,例如,IO指令in/ out,关中断开中断指令cli/sti 如果应用程序直接使用特权指令,报错。如下:
计算机上电先跑操作系统代码,此时CPU状态为00,在将P1调入运行前将CPU状态改为11,然后将CPU让给P1运行,在执行P1是遇到修改控制器(主要是修改硬件控制器的指令)等特权指令就报错。防止APP越过OS直接操控HW。
内核态:从上电到OS执行期间(此时CPU是开放状态,啥指令都能执行)。
用户态:执行完OS代码,开始执行APP指令的时候,此时CPU受限,特权指令无法执行。
OS->APP : CPU=11; APP -> OS : CPU=00。
若用户程序的功能需要特权指令,如IO? 向操作系统发出请求,称为系统调用(System call)。每个操作系统均需要提供一些系统调用,不同操作系统提供的系统调用不一样(这是应用程序不能跨平台运行的重要原因)
7. 处理中断
中断发生时,由操作系统先处理,再转发给相应的用户程序,例如:键盘上按下一个键。回忆:中断控制器、中断处理程序(设备驱动程序提供)、应用程序 。
上述中断为硬件中断(interrupt),陷入(trap)的机制与硬件中断类似,用于实现系统调用,又称为软中断。如果操作系统要“剥夺” 一个程序的CPU,让CPU去执行另外一个程序,如何做到? 让定时器周期性产生时钟中断(时间片)。
综上,再总结一下:操作系统的运行:1. 计算机启动时,启动内核模式、设置中断处理程序、创建用户进程、设置定时器周期性唤醒自己;2. 从内核模式切换到用户模式,运行用户程序;3. 中断(硬件或软件)来临时,从用户模式升级为内核模式,操作系统重新掌握CPU,处理完毕后降级为用户模式,运行用户程序。
四、课程安排
1. 管理CPU——进程管理;
2. 管理内存——内存管理;
3. 管理外存——存储管理;
4. 管理外设——I/O;
Linux操作系统内核: