> For the complete documentation index, see [llms.txt](https://b0ldfrev.gitbook.io/note/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://b0ldfrev.gitbook.io/note/linux_operating_system/xi-tong-tiao-yong.md).

# 系统调用

## int 0x80方式

eax寄存器中为调用号，ebx、ecx、edx、esi等寄存器则依次为参数

（1） 由于INT指令发生了不同优先级之间的控制转移，所以首先从TSS（任务状态段）中获取高优先级的核心堆栈信息（SS和ESP）；

（2） 把低优先级堆栈信息（SS和ESP）保留到高优先级堆栈（即核心栈）中；

（3） 把EFLAGS，外层CS，EIP推入高优先级堆栈（核心栈）中。

（4） 通过IDT表项的0x80处，加载CS，EIP（控制转移至中断处理函数）

```
STACK 
----------------------
返回用户态的EIP
用户态的CS
用户态的EFLAGS
用户态的ESP
用户态的SS（和DS相同）
```

然后就进入了中断0x80的处理函数system\_call了，在该函数中首先使用了一个宏SAVE\_ALL，该宏的定义如下所示：

```
#define SAVE_ALL / 
cld; / 
pushl %es; / 
pushl %ds; / 
pushl %eax; / 
pushl %ebp; / 
pushl %edi; / 
pushl %esi; / 
pushl %edx; / 
pushl %ecx; / 
pushl %ebx; / 
movl $(__KERNEL_DS),%edx; / 
movl %edx,%ds; / 
movl %edx,%es;
```

该宏的功能一方面是将寄存器上下文压入到核心栈中，对于系统调用，同时也是系统调用参数的传入过程。 因为在不同特权级之间控制转换时，INT指令不同于CALL指令，它不会将外层堆栈的参数自动拷贝到内层堆栈中。所以在调用系统调用时，必须先象前面的例子里提到的那样，把参数指定到各个寄存器中，然后在陷入核心之后使用SAVE\_ALL把这些保存在寄存器中的参数依次压入核心栈，这样核心才能使用用户传入的参数。

接着调用system\_call\_table

```
call *SYMBOL_NAME(sys_call_table)(,%eax,4) 
```

这里是以EAX（即前面提到的系统调用号）作为偏移，在系统调用表sys\_call\_table中查找处理函数入口地址，并跳转到该入口地址。

## sysenter 方式

在新版本的内核中，加入了sysenter方式，sysenter 指令之前有一个 mov edx，esp的操作，后续进入Ring0通过edx定位参数

关键是在 Ring3 的代码调用了 sysenter 指令之后，CPU 会做出如下的操作：

1． 将 SYSENTER\_CS\_MSR 的值装载到 cs 寄存器

2． 将 SYSENTER\_EIP\_MSR 的值装载到 eip 寄存器

3． 将 SYSENTER\_CS\_MSR 的值加 8（Ring0 的堆栈段描述符）装载到 ss 寄存器。

4． 将 SYSENTER\_ESP\_MSR 的值装载到 esp 寄存器

5． 将特权级切换到 Ring0

6． 如果 EFLAGS 寄存器的 VM 标志被置位，则清除该标志

7． 开始执行指定的 Ring0 代码 ,同上int方式的接口函数 system\_call

在 Ring0 代码执行完毕，调用 sysexit 指令退回 Ring3 时，CPU 会做出如下操作：

1． 将 SYSENTER\_CS\_MSR 的值加 16（Ring3 的代码段描述符）装载到 cs 寄存器

2． 将寄存器 edx 的值装载到 eip 寄存器

3． 将 SYSENTER\_CS\_MSR 的值加 24（Ring3 的堆栈段描述符）装载到 ss 寄存器

4． 将寄存器 ecx 的值装载到 esp 寄存器

5． 将特权级切换到 Ring3

6． 继续执行 Ring3 的代码
