我在 youtube 上看到了一个视频,其中每个寄存器都存在系统调用。那么这是否意味着系统调用存储在寄存器中?如果是这样,这怎么可能,我的意思是它们由控制操作系统的内核访问。那么内核如何访问寄存器以及内核如何知道哪个系统调用存在于哪个寄存器中?
答案1
没有“每个寄存器的系统调用......”。
系统调用接口依赖于硬件架构。通常(几乎总是?),进程将与所需系统调用相对应的整数存储在预先确定的寄存器中,然后执行一条指令,触发CPU将执行转移到内核的系统调用处理代码。内核引用预先确定的寄存器来识别进程正在请求哪个系统调用,然后执行适当的系统调用处理程序。
例如,考虑 Linux x86_64 程序集中的“hello world”程序:
$ cat hello.s
.text
.global
main:
movq $1, %rax # write() system call number
movq $1, %rdi # first parameter -- standard output file descriptor
movq $msg, %rsi # second parameter -- buffer
movq $14, %rdx # third parameter -- buffer length
syscall # invoke system call
movq $60, %rax # exit() system call number
movq $0, %rdi # first parameter -- exit status
syscall # invoke system call; this will never return
.section .rodata
msg:
.ascii "Hello, world!\n"
$ gcc hello.s
$ ./a.out
Hello, world!
$
对于x86_64,系统调用号存储在寄存器中rax
,系统调用的第一个参数存储在 中rdi
,系统调用的第二个参数存储在 中rsi
,第三个参数存储在 中rdx
。
该示例首先将值放入1
寄存器中rax
。 1
是系统调用的系统调用号write()
。接下来,1
放入寄存器rdi
;1
是标准输出的文件描述符,rdi
是第一个参数的寄存器。然后,将 的地址msg
存储到寄存器 中rsi
;rsi
是第二个参数。接下来,14
存储在rdx
; 14是字符串的长度,rdx
是第三个参数的寄存器。然后该syscall
指令触发CPU将控制权转移给内核代码。内核检查rax
,使用该寄存器中的值 (1) 来确定要执行(写入)的系统调用,然后调用适当的处理程序。
当内核执行完系统调用后,控制权返回给用户空间进程。它写入60
寄存器rax
; Againrax
是系统调用号的寄存器,这里60
是 的系统调用号exit()
。它写入0
寄存器rdi
——退出系统调用的第一个(也是唯一的)参数;退出状态。该指令再次syscall
触发 CPU 将控制权转移给内核代码。内核检查rax
,使用该寄存器中的值(60)来确定要执行(退出)的系统调用,然后调用适当的处理程序。处理exit()
程序会破坏该进程,因此该指令永远不会返回。
执行相同任务的示例因硬件架构而异;甚至对于 32 位 Intel 来说也不一样。