本页介绍了 Starnix 如何将 Zircon 异常和信号转换为未修改的 Linux 程序的 Linux 信号。
设计初衷
Linux 程序依赖信号进行异步通知(例如 SIGALRM、SIGCHLD)和错误处理(例如 SIGSEGV、SIGCHLD)。由于 Starnix 在 Zircon 进程中运行 Linux 程序,因此必须弥合 Zircon 的事件模型与 Linux 的信号模型之间的差距。
如需详细了解信号转换背后的动机和 Starnix 设计理念,请参阅她说。
信号流
Starnix 中信号的生命周期可分为三个不同的阶段:

“Delivery”(提交)
Starnix 中的信号传递需要将 Zircon 的异常处理机制与 Linux 信号范式桥接起来。Starnix 会拦截事件,并确保它们以 Linux 信号的形式到达正确的目标任务。
投放可以由多个不同的来源触发:
- Zircon 异常:最复杂的路径涉及硬件故障。当以受限模式执行的 Linux 线程触发 CPU 异常(例如段错误或除以零错误)时,Zircon 会捕获此违规行为,并通过受限退出代码将其路由回 Starnix 内核。
- 内部状态转换:Starnix 会持续监控执行环境。内部组件(例如计时器子系统或进程管理服务)通常会根据状态变化自主触发信号(例如,计时器到期触发
SIGALRM,或子进程退出触发SIGCHLD)。 - 系统调用:任务可以通过直接进行
kill(2)或tgkill(2)等系统调用,明确请求向其他进程或线程组传送信号。
正在处理
成功传递信号并将其附加到任务的待处理队列后,内核必须确定如何处理该信号。这种情况发生在处理阶段,此时 Starnix 会评估任务的已注册信号操作。
任务在运行时使用 rt_sigaction(2) 定义这些操作。根据配置,Starnix 将执行以下三种不同的路径之一:
- 忽略:安全地舍弃信号,不会中断线程的执行流程。
- 默认操作:如果未定义自定义处理程序,Starnix 会针对相应信号执行 Linux 默认解决方案。这可能意味着静默终止进程、生成用于调试的核心转储、暂停执行或只是继续执行。
- 自定义处理程序:如果应用已注册用于处理信号的特定函数,Starnix 必须准备线程的用户空间堆栈,以便安全地中断当前工作负载并切换到自定义处理程序例程。
执行
当需要自定义处理程序时,Starnix 会执行以下操作:
- 状态保留:它首先提取并保留线程的当前中断寄存器状态,然后将其保存到线程的用户空间堆栈中。
- Sigframe 构造:然后,它会在堆栈上围绕此保存的状态构造
sigframe数据结构。此框架包含稍后恢复线程所需的所有必要上下文。 - 重定向:保存备份后,Starnix 会修改线程的指令指针,将其重定向到应用的已注册信号处理函数所在的内存地址。
- 恢复:最后,Starnix 将控制权交还给线程,在受限模式下恢复执行。线程在处理程序内部“唤醒”。
相关系统调用
以下系统调用是任务用于配置和触发上述信号流的主要接口:
rt_sigaction(2)
任务使用此系统调用来注册特定信号的自定义操作。Starnix 在 Task 结构中维护此映射,并在处理阶段对其进行评估,以确定信号应被忽略、默认处理还是路由到用户空间执行处理程序。
rt_sigreturn(2)
当用户空间信号处理程序完成执行时,它会调用 rt_sigreturn。Starnix 使用此信号来结束执行阶段。它会读取之前推送到堆栈上的 sigframe,恢复线程的原始中断状态,并无缝恢复正常运行。
kill(2)
此系统调用允许任务主动向其他进程发送信号。Starnix 会验证必要的权限,并充当传送阶段的事件源,将请求的信号附加到目标进程的待处理信号队列中。