【发布时间】:2016-07-19 07:57:08
【问题描述】:
我一直在研究 x86-64 的 ABI、编写汇编以及研究堆栈和堆的工作原理。
给定以下代码:
#include <linux/seccomp.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
// execute the seccomp syscall (could be any syscall)
seccomp(...);
return 0;
}
在 x86-64 的汇编中,这将执行以下操作:
- 对齐堆栈指针(因为它默认关闭 8 个字节)。
- 为调用
seccomp的任何参数设置寄存器和堆栈。 - 执行以下程序集
call seccomp。 - 当
seccomp返回时,据我所知,C 很可能会调用exit(0)。
我想谈谈上面第三步和第四步之间发生的事情。
我目前有我的堆栈用于当前正在运行的进程,它在寄存器和堆栈上都有自己的数据。用户空间进程如何将执行权交给内核?内核是否只是在调用时拿起然后从同一个堆栈推入和弹出?
我相信我在某处听说过系统调用不会立即发生,而是在某些 CPU 滴答声或中断时发生。这是真的?例如,这在 Linux 上是如何发生的?
【问题讨论】:
-
对于函数调用,你是对的。但是,对于系统调用,没有。 函数
seccomp()会将参数从堆栈中加载到特定的寄存器中以特定的顺序,将seccomp的系统调用号加载到rax,然后执行指令int 0x80、sysenter或syscall。 CPU“捕获”并将控制权交给内核,内核执行中断服务例程,确定编号为rax的系统调用被请求并执行。内核在将控制权返回给进程时,会在rax中报告返回值。 -
@IwillnotexistIdonotexist 很好的回复,应该是答案!
-
@PeterCordes 对我的评论进行了出色的阐述。正如他所指出的,CPU 不会等到下一个滴答声。如果 CPU 被中断,它会尽快处理中断(由于上下文切换,在几百个周期内)。不同之处在于程序可以通过
syscall指令自愿中断自己,或者计时器可以不自觉地中断程序的执行以将控制权交给内核。内核设置此计时器的目的是为了定期干预以在进程之间共享 CPU 时间片 -
啊,现在涉及到 1) 跳转到处理系统调用的 ISR 2) 将线程的所有几十个寄存器值保存到一个簿记结构中 3) 用内核线程的替换它们寄存器值。然后系统调用的实现被调用,运行,返回,然后按照相反的过程切换回用户模式,除了
rax的值被故意替换为系统调用的返回码。这是用户模式进程所期望的,并且是用户模式和内核模式之间的 ABI(合同或承诺)的一部分。 -
@IwillnotexistIdonotexist:系统调用期间的用户->内核转换确实保存了用户空间寄存器,但没有单独的内核线程可以切换和恢复寄存器。内核入口点代码确实修改了 RSP 以指向该线程的内核堆栈,但当入口点 asm 运行 CALL 指令以调用 C 系统调用处理代码时,其他寄存器保留保存系统调用参数或垃圾。见the source in
arch/x86/entry/entry_64.S
标签: c assembly linux-kernel kernel