用户和内核虚拟地址的关系在之前的几个问题中已经讨论过(下面链接),但是据我了解,用户进程不能读取或写入内核地址。
所以,如何用户进程是否共享和接收来自内核的数据?
是通过记忆吗?如果是这样,内存布局在哪里?也许CPU寄存器?
相关问题:
答案1
让我们考虑一个例子:一个简单的 x86你好世界Linux 程序,打印一条消息stdout
并退出。它需要将几个数据项传递给内核:
- 要输出的文本字符串,
- 退出代码。
这是汇编代码(用 FASM 编译):
format ELF executable
segment readable executable
; system call numbers
SYS_EXIT=1
SYS_WRITE=4
; file descriptors
STDOUT=1
entry $
start:
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, message
mov edx, messageLength
int 0x80
mov eax, SYS_EXIT
xor ebx, ebx ; exit code 0
int 0x80
message:
db "Hello, world!",0xa
messageLength=$-message
该程序为实现其主要目标(消息输出)所做的所有工作是
sys_write
将适当的 CPU 寄存器设置为表示系统调用号(对于syscall)、文件描述符 (stdout
)、消息地址和消息长度的值- 执行系统调用,在本例中通过软件中断 0x80
类似的顺序是退出:将寄存器设置为系统调用号和退出代码,然后执行系统调用。
哪些寄存器要设置哪些值由系统调用定义调用约定。
内核开始执行系统调用处理程序后,该处理程序从应用程序的上下文中读取寄存器的值,并根据调用约定解释它们。特别是,当它看到系统调用是 时sys_write
,它会获取消息的长度和地址,并使用它们从用户空间内存中读取。然后这些数据(以及文件描述符)被传递给将执行实际工作的驱动程序。
答案2
用户进程无法读取或写入内核地址
不,但是内核可以根据需要读取和写入用户地址。 Linux 系统调用传递系统调用号和 CPU 寄存器中的参数。 (查找诸如“Linux 系统调用调用约定”之类的内容。)
其中一些参数可能是指针,在这种情况下,内核知道在用户地址空间中的指向位置中查找数据。据我了解,内核实际上在使用数据之前将其复制到内核空间。 (否则另一个用户空间线程可能会在系统调用期间修改数据。)但数据的位置可以根据程序的要求位于用户地址空间中的任何位置。