OS运行原理
操作系统是什么
内容介绍:
- 在操作系统main函数的核心代码只有两行:
for(;;) pause();//pause函数表示将进程挂起,直到收到信号才唤醒进程 - 其余部分,像IDT(中断向量表,一个函数指针数组)、系统调用表,异常处理方法等,大多都是函数。
所以可以得出如下结论:
- 操作系统一运行起来就被挂起(pause),然后什么都不做。
操作系统如何工作
工作流程介绍:
- CPU内部集成了一个名叫时钟源的硬件,它会按照一个稳定的频率刺激CPU特定部位(针脚)。CPU一但受到该刺激,首先会把当前正在运行的进程的上下文(也就是寄存器的内容)保存到进程PCB里(防止执行其他任务时把数据覆盖掉),然后CPU拿着时钟源这个硬件对应的中断号(也就是一个数字)去IDT里面找到一个函数(IDT是一个函数指针数组,中断号是下标,可以取出函数指针)。这个函数是一个进程调度服务。它的行为如下:将刚才正在运行的进程的时间片计数器减1,如果时间片变成了0,就重新挑选一个进程执行。执行后如果没有发生进程转换就把刚才保存的上下文恢复到CPU中。
哦!原来OS基于时间片的进程调度功能是通过时钟源硬件+函数实现的,而时间片就是时钟源跳动一次的时间间隔。 - 除了时钟源的其他硬件,比如:键盘、鼠标、磁盘、显示器... ...一但遇到特定事件,比如:用户点击键盘、磁盘数据传输完成,就会通知CPU中的中断控制器,中断控制器会通知CPU,此时CPU拿到一个特定的中断号,带着这个中断号,CPU继续查找IDT,保存上下文,执行函数,恢复上下文... ...
哦!原来OS不会轮询所有硬件来查看硬件是否准备完成,是否有数据,而是硬件发生变化后向CPU发送中断,不同硬件的中断对应不同的中断号,也就会执行不同的处理函数,然后对硬件进行读取,写入等操作。
所以我们可以大胆认为:
- 操作系统大概就是一个中断响应程序,是一个几乎什么都不做的懒汉,甚至就是一个函数集合。当然,实际不止这些,操作系统还会再启动的时候就fork出固定类型的子进程,这些子进程和普通进程一样被调度,但他们做的是操作系统的一些管理工作,比如定时把内存数据刷新到磁盘上,这不就是我们平时所说的操作系统的XX管理功能吗。
系统调用
- 为了让操作系统可以服务用户,操作系统提供系统调用,且不让用户以其他任何方式随便访问内核,以保证自己的安全性。
- 既然硬件可以刺激CPU,那同样也可以给CPU指定一条指令(system call / int 0x80)。只要遇到这条指令,CPU就会从固定寄存器取出一个数字充当中断号,然后去IDT中查找相应的中断服务并执行。根据中断号,CPU执行了处理软中断这个服务(函数),它的行为如下:从特定寄存器中取出一个数字当作偏移量,然后把它作为下标并从系统调用表里找到一个函数指针,并执行这个函数(也就是执行系统调用,参数会通过寄存器传给系统调用)。
哦!原来想要调用系统调用很简单:给CPU寄存器写入中断号,写入想执行的系统调用在系统调用表中的偏移量,然后执行中断指令,触发软中断处理服务,从而找到并执行系统调用 - 所以执行系统调用根本不需要函数地址(区别于用户函数),只需要中断号和函数偏移量。所以C语言库中封装的所有系统调用其实就是:
move eax 系统调用偏移量 system call [中断号] 这叫做软中断
异常处理
如果我们的程序,访问了野指针,或者是进行了除0运算等,进程就会直接被杀死。其原理如下:
- CPU进行计算后,如果计算溢出了或者是进行了除0这样的非法操作,会把相应寄存器上的相应标志位置1,等到下一条使用这次计算结果的指令执行时会检查标志位,如果发现不正常,会触发软中断,也就是执行IDT中相应的异常处理服务,该服务最终可能就会给造成异常的进程发送死亡信号,进程处理信号的时候就终止自己
- CPU访存时,如果MMU查询页表项失败,就会触发缺页异常,也执行IDT中相应的异常处理服务,可能就会给进程发送死亡信号。之所以说是可能,是因为缺页异常也有可能是懒加载导致的,这种情况不会终止进程而是会把数据加载到内存上。
内核态和用户态
- 一但执行系统调用,就会把CS寄存器上的标志位变成0(内核态),等到系统调用结束后再把标志位变成3(用户态)
- 当我们在用户态却想要访问内核区,仍然要经过MMU的虚拟->物理地址的转换,此时MMU发现用户访问的是3-4G的内容但CS标志位不是0,就会产生错误,以此来进行权限控制
- 所有进程都映射到同一个操作系统,因此都被同一个操作系统管理。(如果内存上有两个操作系统,让进程映射不同的操作系统,这就是虚拟机的一部分原理)
