参考《程序员的自我修养》第十二章:系统调用与API(这章篇幅很短,20多页,1个小时便可以读完)
一、系统调用与库函数
1.系统调用函数是系统内核抛出来给用户空间调用的接口,系统调用由用户态调用,在内核态执行。系统调用的功能广泛,例如创建/退出进程和线程、进程内存管理、对系统资源访问如文件、网络、进程间通信、硬件设备等。
Linux上使用0x80号中断作为系统调用的入口;
Windows上采用0x2E作为系统调用的入口;
2.库函数:在函数库文件中实现,执行只需要在用户态执行就可以。
3.二者区别:
库函数在库文件中
系统调用函数:在系统内核实现
库函数有可能还需要转调系统调用函数,比如:fopen,printf等,也有可能不需要转调系统调用,比如strlen,strcpy等
二、Linux系统调用
1.概念
系统调用由0x80中断完成,各个通用寄存器用于传递参数,EAX寄存器用于表示系统调用的接口号;
EAX=1 表示退出进程(exit)
EAX=2 表示创建进程(fork)
EAX=3 表示读取文件或IO(read)
每个系统调用 对应一个 内核源代码中的一个函数(以sys_开头,比如exit调用对应内核的sys_exit函数,系统调用返回时EAX又作为调用结果的返回值)
(后面会有详细实例说明,但要弄懂整个过程,还需只要下面的几个概念)
2.系统调用弊端:
(1)使用不便:需要记很多函数细节,没有包装
(2)各操作系统之间不兼容
3.运行库的提出:
因为系统调用的一些弊端,所以:“解决计算机的问题通过增加中间层来实现”
这里插入一个c语言运行库的概念:
c程序要想正常运行,要有一套代码集合来支撑,这套代码包括:入口函数及其所依赖的函数所构建的集合,这样的集合成为运行时库。C语言的运行库成为CRT(C RuningTime Library);CRT中包含c标准库,c标准库又包含了24个c头文件(stdio.h、string.h、math.h stdlib.h time.h assert.h ....等等)
三、系统调用原理
1.特权级别的概念:
特权级别:在现代操作系统中有两种特权级别,分别是用户模式(user mode)和内核模式(kernel mode),也成用户态和内核态。
特权模式存在意义:
特权模式的存在,使操作系统在不同模式下运行不同代码,限制代码的权力,提高安全性和稳定性。
例如普通应用程序在用户态的模式下就会有很多操作受限制比如访问硬件设备等。
2.中断:
中断:是一个软件或者硬件发出的请求,要求CPU暂停当前的工作转手去处理更加重要的事情。
操作系统一般通过中断来从用户态切换到内核态。
3.中断的两种类型:
(1)硬件中断:电源掉电、键盘按下等等
(2)软件中断:通常是一条指令(在i386下是int),带有参数记录中断号,使用该指令用户可以手动触发某个中断并执行其中的中断处理程序。(例如在i386下,int 0x80这条指令,会调用第0x80号中断处理程序)
4.中断的两个属性
(1)中断号:例如0x80
(2)中断处理程序
在内核中,有一个中断向量表的数组:第n项包含n号中断的中断处理程序指针。
因为中断号有限,操作系统不会用一个中断号对应一个系统调用,所以在Windows下,绝大数系统调用有0x2E触发;
在Linux下使用0x80触发所有系统调用。
那么系统如何知道这个0x80中断触发的哪一个系统调用呢???(如上图,eax寄存器啊!再根据系统调用表)
当然知道,因为每一个系统调用都有一个系统调用号(系统调用号在系统调用表中存放);例如Linux中fork的系统调用号是2,在执行0x80前2会存放在EAX寄存器中,对应的中断代码取得这个2,然后执行正确的函数(sys_fork),并且执行后的返回值也有EAX寄存器带回。
(图片截取《程序员的自我修养》390页)
书中太剖析的触发0x80中断详细过程:(1)触发中断(2)切换堆栈(3)中断处理程序
......
......
......
四、Windows API
1.API:application programming interface 应用程序编程接口。API是对一系列事物的总称。在Windows下提到API时,一般就是指Windows提供给应用程序的接口,相当于Linux下的系统调用。
Linux下,系统调用是应用程序的最底层。Windows下,最低层的接口是Windows API。
如图一个fwirte的调用路径
从图中也可以看出,在触发中断时,Linux和Windows的区别,Linux下0x80中断在运行库中的系统调用之后触发,Windows多了一层API的封装,在API层中触发0x2E中断。
并由此可见,CRT(c运行时库,是在Windows API的上层)。