OS的中断、异常、系统调用

Posted by 刘知安 on 2019-06-16
文章目录
  1. OS启动流程
  2. 中断、异常、系统调用
  3. 用户态、内核态
  4. 函数调用、系统调用

OS启动流程

在这里插入图片描述

中断、异常、系统调用

在这里插入图片描述

  • 中断:

由外设或硬件触发,如read系统调用后,系统发出读磁盘的操作,当磁盘数据准备好后,向OS发出一个异步通知消息,即中断。一般来说,中断是异步的方式。

  1. 具体某个的中断会有一个中断ID,比如1号中断,10号中断,这是OS识别具体产生了哪个中断的标识。
  2. 而在应用程序部分(或者说软件部分),程序会保存中断发生前一时刻的执行现场,主要是保存一些寄存器中的值,然后去转到中断的服务例程执行中断,
  3. 然后OS再恢复之前保存的处理状态,就好像应用程序不知道中间某处发生了中断,所以说,中断是对应用程序透明的
  • 异常:由应用程序触发,是一种非预期的事件,如0除事件。

异常和中断有点类似又有本质区别,本质区别就是触发的对象不同,异常是由应用程序触发的,中断是由外设触发的。一般来说,异常是同步的方式。

  1. 同样,某个具体的异常也有异常编号ID,异常产生的前一刻也会保存异常现场,然后转到异常处理,
  2. 处理的时候和中断服务例程不太一样,有可能是因为因为应用程序本身的错误(如出现了0除事件),这样一来,OS怎么也不可能解决异常,于是会将该异常的程序kill掉;还有一种情况就是由于OS本身的问题,导致某些应该正常执行的程序发生了异常,OS会尝试解决异常,并重新执行产生异常的指令(也就是我们的应用程序啦)。
  3. 恢复现场
  • 系统调用:由应用程序触发,应用程序向OS请求某个服务,如read操作。系统调用是异步或同步的方式,主要是要看我们是从哪个出发点看待的。
    在这里插入图片描述
    例如一个应用程序中的printf()代码,会转到write系统调用(syscall),程序访问syscall主要是通过API的方式,例如在Windows上有WIN32 API,而在UNIX、linux和mac os上,是通过POSIX API来访问syscall。

用户态、内核态

用户态和内核态都是针对CPU运行状态来说的,在用户态下,CPU无法执行一些特权指令,而在内核态下是可以的。

系统调用和函数调用的区别:

函数调用、系统调用

函数调用是在用户态下的调用,堆栈也只涉及到用户堆栈下的切换;而系统调用不太一样,系统和用户都有一个自己的堆栈,所以会存在堆栈切换的开销,跨越了OS的边界,但是比较安全的。

举个栗子,你写了一个函数f1()f2(),两者之前存在一个调用,这样其实你只是在你自己的堆栈下切换,这样切换是比较快的。而如果你写了一个f3(),其中写了一个printf()函数,这样就会涉及系统和用户的堆栈切换了。

系统调用指令:INTIRET,涉及堆栈切换和特权级的切换

函数调用指令:CALLRET,常规调用没有堆栈切换

跨越OS边界带来的开销:

  • 需要建立中断/异常/syscall调用好与对应服务例程映射关系表。
  • 需要建立系统内核堆栈
  • 通过API访问syscall时,需要验证参数
  • 内核态执行完成返回到用户态时,需要把一些参数复制到用户空间,因为用户空间和内核空间是分开的。
  • 内核态独立地址空间TLB