从故事的开始说起 —— 最开始”中断”解决了哪些问题
-
计算机启动的过程
-
按下电源后,主板开始给CPU通电,待电源稳定后,CPU把CS寄存器的值设置为0xFFFF,IP寄存器的值设置为0x0000;表示CPU要执行0xFFFF处的指令,这个地址指向主板的ROM中。
-
0xFFFF指令一般为jmp指令,用以跳转到BIOS在ROM中真正的位置
-
BIOS就是一段启动代码。过程中,首先要开机自检(内存,显卡,声卡,网卡等有没有连接成功),之后去磁盘的第一个扇区(MBR master boot record 主引导记录)。将其加载到内存中,并且将系统的控制权交给MBR。
-
MBR首先搭建起文件系统来,有了文件系统后才能找到OS内核代码并负责从磁盘中加载OS内核到内存中,然后将系统控制器交给操作系统。
以上并不是要说明的重点,但为了故事有头有尾就又记了一遍
- 之前的系统全部处于实模式(real mode),内存访问直接根据物理地址进行;此时将实模式转化成保护模式(Protected mode 全称是虚拟地址保护模式);
- 之后由操作系统负责加载各个程序,也就是各个进程,计算机开始全力工作。
-
-
保护模式
-
为什么要从实模式变成保护模式?
内存中有内核和其他应用程序进程,假如仍然在实模式下,应用程序进程随意访问内核和其他程序的任意数据和指令,只需要提前知道目标数据所在的物理地址。更可怕的是可以随意修改,这就太危险了。
-
保护模式
不直接使用物理地址,而是使用虚拟地址,程序执行时CPU负责将虚拟地址转化成物理地址。
- 由于每个进程内,可见的地址都是通过虚拟地址所指的(或者是在编码层面所有指针也都是虚拟地址),一个进程以为自己能访问到全部,但其实在地址转换中,对超出自身内存范围的指针是无法访问到的;以此来提供进程间安全的保证。
- 于是可以得出结论:进程间是隔离的,进程和内核间也是隔离的。
- 问题:
-
进程间隔离,那进程间通信怎么办?
-
进程和内核隔离,那系统调用怎么办?
答案是:通过中断,安全的,绕路的 完成系统调用
-
-
中断
中断分为硬中断和软中断,解决上述问题的是软中断,硬中断用来解决进程切换问题
-
IDT 中断向量表
CPU中记录有IDTR 中断描述符表寄存器,用来记录内存中中断描述符表的具体位置
IDT类似一个map,key是0 - 255,value是所指内核程序的第一条指令的地址
其中,0x80指向系统调用程序
-
以一个简单代码为例说明中断的执行(软件中断)
print("Hello World")编译器首先会编译成机器语言
put "write()" eax put "hello" ebx int 0x80代码表示 将write放在eax寄存器上,hello放在ebx寄存器上,之后触发中断(interrupt)此时CPU从用户态切换到内核态,中断代码是IDT 0x80所指位置;而0x80就是指向System Call系统调用
之后操作系统获取到寄存器上的系统调用方法名(或编号)和参数,从系统调用表中找到这个方法的具体位置,即可执行成功。也就是说,用户进程调用内核代码的方式是:通过系统调用表中共识的编号,完成函数调用。
详细过程见博文:blog.csdn.net/u012503639/…
-
硬件中断: 根据事件的发出者不同,可以将中断分为硬件中断和软件中断两种;软中断就是上文所述有CPU在处理某个特定指令时触发的吗。而硬中断则是由处理器外部的设备触发的电子信号。
电子手表就像一个简单的计算机,中间有简单的类似CPU的累加器;手表核心有一块设备叫晶振,其作用是能够让手表的时间匀速增加;
实现方式是:晶振的特性是:通入直流电,输出频率相同的信号,以此模拟时间匀速流动。
而硬中断的实现方式也基于此,固定时间片后强制让CPU切换运行线程。
-
中断的成本
有研究测试表示:系统调用与函数调用相比(C语言),消耗的时间平均为十几倍。
- 对于软中断而言,CPU由原本执行用户程序切换成执行内核程序,那势必要把原本用户进程的数据保存下来(保存现场);我觉得只需要把CPU内寄存器的数据保留即可,系统调用用不到三层缓存吧?
- 对于硬中断而言,CPU内寄存器和三级缓存的数据必然都要保护起来,所以成本会更高。
于是由于软中断的成本不低,系统调用又频繁的发生在任何操作中,这就导致程序的性能有所影响。 那么接下来的优化思路就是:如何减少系统调用的次数。
- Java中我们最熟悉的优化:synchronized的锁升级过程就是为了避免频繁的加锁释放锁
- IO中总是使用buffered...stream或者buffered...channel,是为了避免频繁的向socket中write和read
- 操作系统IO多路复用模型中,epoll比select和poll高级的一点也在于减少了系统调用
- redis使用 mset 代替 set 进行批量操作,也是为了避免过多文件IO
- ......