多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] ## 系统调用 * **内核提供的接口称为`系统调用(指运行在用户空间的程序访问操作系统内核所提供服务的接口(内核服务意味着更高的权限)`,是代码方式,即表示用户(非编程人员)不能直接与内核进行交互。** * **系统调用是基于CPU指令集的,进行`封装、扩展(比如扩展中断向量,实现一系列的系统调用)`。类似于语言库中的API对系统调用的封装。** * 调用系统调用的两种方式:以Linux为例(其他系统也是类似),在 Linux 平台下有两种方式来使用系统调用: * `利用封装后的 C 库(libc)`。Linux、Windows、iOS等许多系统内核大部分都是C语言编写的,其中也有少部分的C++、C#等语言,所以大部分的系统调用通常是C、C++语言格式的 * `通过汇编直接调用`。通过汇编语言来直接调用系统调用,是最高效地使用 Linux 内核服务的方法,因为最终生成的程序不需要与任何库进行链接,而是直接和内核通信。 * 第一种方式是对第二种方式的封装。如DOS、Linux 的系统调用都是通过中断汇编指令(int 0x80)来实现的。(见下面—系统调用的实现) ## 系统调用的实现 系统调用接口在实现中往往以`软件中断(Software interrupt),简称INT(软中断)`的方式提供,比如 Linux 使用 `INT 0x80`(INT是汇编指令,0x80为参数,表示80号中断)作为系统调用接口,Windows使用 0x2E 号中断作为系统调用接口(从Windows XP Sp2开始,Windows开始采用一种新的系统调用方式) 以Linux为例,在进行系统调用的时候: * 发起int 0x80中断,并传入`系统调用号` * 系统调用号:有一个`sys_call_table`是一个全局函数数组,存储所有系统调用的地址(可以查看`include/linux/sys.h`文件),系统调用号是系统调用在该数组中的下标。 * 调用`set_system_gate`函数处理中断,首先从当前的用户态切换到内核态,然后找到中断向量表,根据中断编号(0x80),找到对应的中断处理程序 * 找到中断处理程序`system_call`(在`linux/kernel/system_call.s`中,是汇编程序),根据系统调用号在`sys_call_table`表中找到相应系统调用对应的函数入口。 * 调用`call汇编指令`,输入函数地址 ## 关于中断向量的一些补充 计算机启动时,引导扇区、系统内核会陆续的将一些中断填入中断向量表,并将中断向量指向自己编写的中断服务程序。 如操作系统启动过程的最后,由`head.s`进入`main.c`中,会进行一系列的初始化:内存、设备、时钟、中断等。其中: * `trap_init()`函数,会初始化一些中断向量 * `sched_init()`函数中会调用`set_system_gate(0x80,&system_call)`,设置中断向量号0x80的中断描述符 系统也会为用户保留一些中断向量,用户可以将自己的中断服务程序写入这些中断向量中。不仅如此,用户还可以自己更改和完善系统已有的中断向量。`相当于给INT指令增加参数(中断向量号)选项,且指定对应的调用程序` **引导扇区(实模式) → bootloader(实模式 → 保护模式,R0) → 操作系统(保护模式,R0) → 操作系统先加载的是内核,内核处于R0,shell已经是在用户态了** ## linux的系统调用的步骤 在汇编程序中使用Linux系统调用。 需要采取以下步骤在程序中使用Linux系统调用 * 将系统调用号放在EAX寄存器中。 * 将参数存储在寄存器EBX,ECX等中的系统调用中。 * 调用相关的中断(80h)。 * 结果通常在EAX寄存器中返回。 有六个寄存器存储所使用的系统调用的参数。 这些是EBX,ECX,EDX,ESI,EDI和EBP。 这些寄存器采用连续参数,从EBX寄存器开始。 如果有超过六个参数,则第一个参数的存储单元存储在EBX寄存器中。 以下代码片段显示了系统调用sys_exit的用法 ``` mov eax,1 ; system call number (sys_exit) int 0x80 ; call kernel ``` 以下代码片段显示了使用系统调用sys_write ``` mov edx,4 ; message length mov ecx,msg ; message to write mov ebx,1 ; file descriptor (stdout) mov eax,4 ; system call number (sys_write) int 0x80 ; call kernel ``` 所有系统调用都列在`/usr/include/asm/unistd.h` ,以及它们的编号(在调用int 80h之前放入EAX的值)。 下表显示了本教程中使用的一些系统调用 | EAX% | 名称 | EBX% | %ECX | %EDX | ESX% | EDI% | | --- | --- | --- | --- | --- | --- | --- | | 1 | sys\_exit | int | \- | \- | \- | \- | | 2 | sys\_fork | struct pt\_regs | \- | \- | \- | \- | | 3 | sys\_read | unsigned int | char \* | size\_t | \- | \- | | 4 | sys\_write | unsigned int | const char \* | size\_t | \- | \- | | 5 | sys\_open | const char \* | int | int | \- | \- | | 6 | sys\_close | unsigned int | \- | \- | \- | | 以上是Intel规范的汇编代码 ps:https://www.jianshu.com/p/2b21cffda10d