本页介绍了 Starnix 如何处理 Linux 系统调用。 (系统调用)。
Zircon 进程中 Linux 程序的地址空间
Linux 程序必须先加载到 Zircon 中,然后才能进行任何系统调用。
过程。starnix_kernel
是负责执行上述两项操作的组件
在 Fuchsia 中加载和执行 Linux 程序。
starnix_kernel
首先创建一个 Zircon 进程,其地址空间
分成了两半。上半部分是共享地址空间,下半部分是
私有地址空间然后,starnix_kernel
会设置
Zircon 按以下方式处理:
- Linux 程序被加载到进程的私有地址空间(下半部分)。
starnix_kernel
本身会被加载到进程的共享地址空间(上半部分)。
地址空间的 Linux 部分的构建方式与 与普通 Fuchsia 进程一样,以便与 Linux 的预期布局保持一致 计划。
图 1. 使用 Linux 程序加载的 Zircon 进程的地址空间
和 starnix_kernel
。
共享的 Starnix 实例
每个 Linux 程序都加载到一个单独的 Zircon 进程中,每个 Linux
线程在专用的 Zircon 线程中运行。然而,所有 Zircon 进程
Starnix 容器共享同一个 starnix_kernel
实例。
此共享 starnix_kernel
实例管理 Linux 程序的所有状态
例如文件描述符表、全局虚拟文件系统、
以及运行线程表
如需详细了解 Zircon 进程之间的共享状态
运行 starnix_kernel
的应用,请参阅受限模式 RFC 和
ShareableProcessState
结构体。
图 2. starnix_kernel
实例在所有 Linux 进程之间共享
在同一个容器中运行
在正常执行模式下,这些进程中的任何线程都可以访问 共享地址空间不过,在受限执行模式下 访问自己的进程的私有地址空间。也就是说, Linux 程序正在某个进程中运行,Zircon 限制其线程访问 只访问进程中地址空间的 Linux(私有)部分。
图 3. 受限模式会阻止 Linux 线程访问
starnix_kernel
部分。
在受限模式下运行 Linux 程序
Linux 程序加载到进程后,starnix_kernel
会指示 Zircon
启动该流程。该进程开始在starnix_kernel
中执行
地址空间的一部分。
starnix_kernel
用于检查任务状态,以确定进入 Linux 的位置
计划。下面显示的 restricted_enter()
方法用于输入特定的
在 Linux 程序中的位置(有关详情,请参阅
Zircon 中的 zx_restricted_enter
系统调用)。
// Enter the restricted portion of the address space at the location pointed to by `rip`.
restricted_enter(task.registers.rip);
不过,完整的调用比上面所示的要复杂一些。通过
方法将受限模式的完整寄存器状态作为输入,通过单独的
VMO 称为“辅助信息文件”。(如需详细了解用于存储
zx_restricted_state
对象,包含受限使用的架构状态
模式,请参阅 Zircon 中的 zx_restricted_bind_state
系统调用。)
调用 restricted_enter()
方法后,Zircon 会注意到,
正进入受限模式,并将会话设为仅访问不公开模式
地址空间,然后再跳到 Linux 代码。
Linux 系统调用的流程
该进程将继续运行,直到 Linux 程序进行系统调用。通过 执行系统调用的汇编指令因架构而异,但它们 所有这一切都会将控制权移交给 Zircon。当 Zircon 检查线程时 看到线程处于受限模式,而不会处理系统调用 Zircon 马上会跳回斯塔尼克斯。
为了找出 Linux 程序发出的系统调用,Starnix 读取了 Zircon 保存的模式寄存器状态。然后,Starnix 会为此调用处理程序 特定系统调用。此系统调用的结果会保存到受限模式 注册状态。
系统调用返回后,Starnix 再次指示 Zircon 将线程 进入受限模式此循环会一直持续到线程退出。
图 4. Zircon 进程中的 Linux 系统调用流程。
下面是图 4 中流程的伪代码表示法:
loop {
// Enter restricted mode at the instruction pointer address.
restricted_enter(task.registers.rip);
// Update the register state of the task to match what Zircon saved before exiting
// restricted mode.
task.registers = thread_state_general_regs_t::from(&restricted_state);
// Figure out which syscall was made based on the appropriate register value.
let syscall = Syscall::from_number(task.registers.rax);
// Execute the syscall for the current task.
execute_syscall(task, syscall);
// After handling the syscall, check for signals, exit status, etc. before
// continuing the loop.
if let Some(exit_status) = process_completed_syscall(task)? {
return Ok(exit_status);
}
}