Appearance
代码:系统调用参数
内核中的系统调用实现需要找到用户代码传递的参数。因为用户代码调用系统调用包装函数,所以参数最初位于 RISC-V C 调用约定放置它们的位置:在寄存器中。内核陷阱代码将用户寄存器保存到当前进程的陷阱帧中,内核代码可以在那里找到它们。内核函数 argint
、argaddr
和 argfd
从陷阱帧中检索第 n 个系统调用参数,分别为整数、指针或文件描述符。它们都调用 argraw
来检索适当的已保存用户寄存器 (syscall.c
)。
一些系统调用传递指针作为参数,内核必须使用这些指针来读取或写入用户内存。例如,exec
系统调用向内核传递一个指向用户空间中字符串参数的指针数组。这些指针带来了两个挑战。首先,用户程序可能有错误或恶意,可能会向内核传递无效指针或旨在欺骗内核访问内核内存而不是用户内存的指针。其次,xv6 内核页表映射与用户页表映射不同,因此内核不能使用普通指令从用户提供的地址加载或存储。
内核实现了安全地将数据传入和传出用户提供地址的函数。fetchstr
是一个例子 (syscall.c
)。诸如 exec
之类的文件系统调用使用 fetchstr
从用户空间检索字符串文件名参数。fetchstr
调用 copyinstr
来完成艰苦的工作。
copyinstr
(vm.c
) 将最多 max
字节从用户页表 pagetable
中的虚拟地址 srcva
复制到 dst
。由于 pagetable
不是当前页表,因此 copyinstr
使用 walkaddr
(它调用 walk
)在 pagetable
中查找 srcva
,从而产生物理地址 pa0
。内核的页表将所有物理 RAM 映射到与其物理地址相等的虚拟地址。这使得 copyinstr
可以直接将字符串字节从 pa0
复制到 dst
。walkaddr
(vm.c
) 检查用户提供的虚拟地址是进程的用户地址空间的一部分,因此程序无法欺骗内核读取其他内存。一个类似的函数 copyout
将数据从内核复制到用户提供的地址。