Linux 函数调用的用户态与内核态

系统 Linux
当系统调用从用户态到内核态的时候,首先要做的第一件事情,就是将用户态运行过程中的 CPU 上下文保存起来,其实主要就是保存在这个结构的寄存器变量里。这样当从内核系统调用返回的时候,才能让进程在刚才的地方接着运行下去。

在用户态中,程序的执行往往是一个函数调用另一个函数。函数调用都是通过栈来进行的。

在进程的内存空间里面,栈是一个从高地址到低地址,往下增长的结构,也就是上面是栈底,下面是栈顶,入栈和出栈的操作都是从下面的栈顶开始的。

32 位操作系统在 CPU 里,ESP(Extended Stack Pointer)是栈顶指针寄存器,入栈操作 Push 和出栈操作 Pop 指令,会自动调整 ESP 的值。另外有一个寄存器 EBP(Extended Base Pointer),是栈基地址指针寄存器,指向当前栈帧的最底部。

例如,A 调用 B,A 的栈里面包含 A 函数的局部变量,然后是调用 B 的时候要传给它的参数,然后返回 A 的地址,这个地址也应该入栈,这就形成了 A 的栈帧。接下来就是 B 的栈帧部分了,先保存的是 A 栈帧的栈底位置,也就是 EBP。因为在 B 函数里面获取 A 传进来的参数,就是通过这个指针获取的,接下来保存的是 B 的局部变量等等。

当 B 返回的时候,返回值会保存在 EAX 寄存器中,从栈中弹出返回地址,将指令跳转回去,参数也从栈中弹出,然后继续执行 A。

对于 64 位操作系统,模式多少有些不一样。因为 64 位操作系统的寄存器数目比较多。rax 用于保存函数调用的返回结果。栈顶指针寄存器变成了 rsp,指向栈顶位置。堆栈的 Pop 和 Push 操作会自动调整 rsp,栈基指针寄存器变成了 rbp,指向当前栈帧的起始位置。

改变比较多的是参数传递。rdi、rsi、rdx、rcx、r8、r9 这 6 个寄存器,用于传递存储函数调用时的 6 个参数。如果超过 6 的时候,还是需要放到栈里面。

然而,前 6 个参数有时候需要进行寻址,但是如果在寄存器里面,是没有地址的,因而还是会放到栈里面,只不过放到栈里面的操作是被调用函数做的。

以上的栈操作,都是在进程的内存空间里面进行的。

当系统调用从用户态到内核态的时候,首先要做的第一件事情,就是将用户态运行过程中的 CPU 上下文保存起来,其实主要就是保存在这个结构的寄存器变量里。这样当从内核系统调用返回的时候,才能让进程在刚才的地方接着运行下去。

在用户态,应用程序进行了至少一次函数调用。32 位和 64 的传递参数的方式稍有不同,32 位的就是用函数栈,64 位的前 6 个参数用寄存器,其他的用函数栈。

在内核态,32 位和 64 位都使用内核栈,格式也稍有不同,主要集中在 pt_regs 结构上。

在内核态,32 位和 64 位的内核栈和 task_struct 的关联关系不同。32 位主要靠 thread_info,64 位主要靠 Per-CPU 变量。

责任编辑:武晓燕 来源: 51CTO博客
相关推荐

2021-12-20 09:53:51

用户态内核态应用程序

2017-08-16 16:20:01

Linux内核态抢占用户态抢占

2022-03-25 12:31:49

Linux根文件内核

2021-08-31 07:54:24

TCPIP协议

2023-01-06 08:04:10

GPU容器虚拟化

2021-09-17 11:59:21

tcpdump网络包Linux

2021-09-08 10:21:33

内核网络包Tcpdump

2014-07-17 09:55:23

Linux程序计时

2021-08-10 16:50:37

内核内存管理

2022-12-30 07:50:05

无栈协程Linux

2022-04-21 11:26:31

鸿蒙操作系统

2021-10-25 09:53:52

鸿蒙HarmonyOS应用

2021-11-26 15:34:27

鸿蒙HarmonyOS应用

2019-12-12 09:23:29

Hello World操作系统函数库

2009-12-07 09:31:23

Linux系统调用表地址

2024-08-01 17:14:53

2020-10-05 22:05:10

Linux系统编程时序竞态

2021-09-06 17:50:09

内核态操作系统

2022-01-08 20:04:20

拦截系统调用

2021-06-17 07:55:34

线程进程COW
点赞
收藏

51CTO技术栈公众号