《程序员的自我修养》读书笔记——系统调用、API

 
 

系统调用

程序运行的时候,本身是没有权限访问多少系统资源的。系统资源有限,如果操作系统不进行控制,那么各个程序难免会产生冲突。线程操作系统都将可能产生冲突的系统资源保护起来,阻止程序直接访问。比如文件、网络、IO、各种设备等。

系统调用涵盖的功能很广,有程序运行锁必须的支持,如创建和退出进程、线程,进程内存管理,对系统资源的访问等。

Linux系统调用与中断

在x86下,系统调用是通过0x80中断完成,各个通用寄存器用于传递参数。EAX寄存器用于表示系统调用的接口号。

比如:EAX=1表示退出进程,EAX=2表示创建进程,EAX=3表示读文件或IO,EAX=4表示写文件或IO。每个系统调用对应到内核源码中的一个函数,他们都是以sys_开头的,比如exit调用对应内核中的sys_exit函数

如果直接使系统调用会有非常多的问题:

  • 使用不方便,操作系统提供的系统调用接口往往过于原始。程序员需要了解很多跟操作系统相关的细节
  • 各个操作系统之间系统调用不兼容

于是增加一个层来解决,系统调用与程序之间增加一个抽象层。这个层就是前面所说的glibc,或者API.

系统调用原理

这里单单以Linux为例,至于Windows调用原理暂时省略。

用户态、内核态、中断与系统安全

现代操作系统中有两种特权级别,分为用户模式和内核模式。

多个模式存在,那么操作系统就可以让不同代码运行在不同模式下,进而限制代码的权限,提高稳定性、安全性。一般普通程序在用户态,操作会受到限制。系统调用运行在内核态,应用程序基本都是运行在用户态被限制。

用户态的程序通过中断来运行内核态的代码,进而从用户态切换到内核态。

 

比如Linux中int 0x80为例,系统调用号使用eax来传入,用户将系统调用号保存在eax,然后使用int 0x80调用中断,中断服务传给信号就可以从eax取得系统调用号,进行调用相应的函数。

 

切换堆栈

在实际执行中断向量表中的第0x80好中断之前,CPU还要进行堆栈的奇幻,在Linx中,用户态和内核态使用的是不同的栈,两者各自负责各自的函数调用,互不干扰。

在执行0x80中断的时候,程序从用户态切换到内核态。这时相应的栈也必须切换到内核栈,从中断处理函数中返回时,程序当前栈需要从内核栈切换到用户栈。

当前栈是指ESP值所在的栈空间,如果ESP的值位于用户栈范围能,那么程序的当前栈就是用户栈。内核栈同理。并且SS的值需要指向当前栈所在的页。所以用户栈切换到内核栈就是:

  • 保存当前ESP、SS的值
  • 将ESP、SS的值设置为内核栈的值

(反过来同理)

  • 恢复原理ESP、SS的值
  • 用户态的ESP和SS的值保存在内核栈上,

当发生中断的时候,CPU除了进入内核态之外还会做如下事情:

  • 找到当前进程的内核栈(每一个进程都有一个内核栈)
  • 在内核栈中一猜压入用户态的寄存器SS、ESP、EFLAGS、CS、EIP

当内核从系统调用中返回,则调动iret指令回到用户态,iret指令会从内核栈里面弹出SS、ESP、EFLAGS、CS、EIP的值。使得栈恢复到用户态的状态。

 


作者:纸简书生
链接:https://www.jianshu.com/p/4b9c40e6b8ae
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

原文地址:https://www.cnblogs.com/feng9exe/p/10738380.html