starry-signal
路径:
components/starry-signal类型:库 crate 分层:组件层 / StarryOS 信号语义组件 版本:0.3.0文档依据:Cargo.toml、src/lib.rs、src/{types.rs,action.rs,pending.rs}、src/api/{process.rs,thread.rs}、src/arch/*、tests/*、os/StarryOS/kernel/src/{task/{mod.rs,signal.rs,user.rs},syscall/signal.rs,mm/loader.rs,file/signalfd.rs,syscall/io_mpx/select.rs}
starry-signal 是 StarryOS 的信号语义引擎。它定义了信号编号、默认动作、sigaction 状态、pending 队列、线程级/进程级信号管理器,以及把信号处理器压入用户栈并通过 rt_sigreturn 恢复上下文所需的架构细节。
它负责的是“信号应当怎样投递、屏蔽、排队、进入用户处理器”,而不是“谁有权发信号”“收到信号后内核最终怎样终止进程”。后者由 starry-kernel 的 syscall 层和任务层在消费 SignalOSAction 时决定。
架构设计
设计定位
从真实调用关系看,starry-signal 位于三层之间:
- 向上承接
sys_kill、sys_rt_sigaction、sys_rt_sigprocmask、sys_rt_sigreturn等系统调用语义。 - 向内与
Thread/ProcessData结合,决定某个信号应落到哪个线程。 - 向下依赖
ax-cpu::uspace::UserContext和starry-vm,把信号帧压到用户栈并恢复上下文。
因此它不是简单的常量定义 crate,而是 StarryOS 用户态信号路径的核心组件。
模块结构
src/types.rs:定义Signo、SignalSet、SignalInfo、SignalStack。src/action.rs:定义SignalActionFlags、SignalDisposition、SignalAction、SignalOSAction、k_sigaction兼容层。src/pending.rs:定义PendingSignals,管理标准信号与实时信号的排队策略。src/api/process.rs:ProcessSignalManager和SignalActions,承载进程级共享信号状态。src/api/thread.rs:ThreadSignalManager,负责线程级 pending、blocked mask、signal frame 构造和sigreturn恢复。src/arch/*:每个架构各自的signal_trampoline汇编、UContext和MContext布局。
1.3 关键数据结构
Signo:1 到 64 的信号编号枚举,并直接编码默认动作。SignalSet:64 位位图,兼容 Linuxsigset_t传递格式。SignalInfo:对 Linuxsiginfo_t的透明包装。SignalAction:用户态可见的sigaction语义,包括 handler、mask、flags、restorer。PendingSignals:真正的待处理队列。ProcessSignalManager:进程级共享 pending 队列、动作表和线程列表。ThreadSignalManager:线程级 pending 队列、blocked mask、signal stack 和 frame 处理逻辑。arch::UContext/MContext:保存/恢复用户态寄存器现场。
这里最容易被误解的点是 PendingSignals 的“pending”定义。源码已经明确说明,它表示“已投递但尚未处理”的信号,不完全等价于 Linux 文档里“已投递但因屏蔽而待决”的狭义 pending。
1.4 投递与处理主线
完整路径如下:
StarryOS 内核中的对应接线点非常明确:
syscall/signal.rs负责把 syscall 参数转换成SignalInfo、SignalSet和SignalAction。task/signal.rs负责把SignalOSAction转换成“终止当前进程”“中断线程”等内核动作。task/user.rs在每次从用户态返回内核后调用check_signals(),决定是否立刻处理 pending 信号。
1.5 真实实现中的关键语义
- 标准信号只保留一个 pending 实例:
PendingSignals::put_signal()对 1 到 31 号信号会去重。 - 实时信号按信号编号分桶排队:同一信号号 FIFO,不同信号号仍按最小 signal number 优先出队。
ProcessSignalManager::send_signal()会遍历已 注册线程,返回第一个未屏蔽该信号的 TID,用于唤醒。ThreadSignalManager::set_blocked()会强制去掉SIGKILL和SIGSTOP,与 Linux 规则一致。handle_signal()会根据SA_ONSTACK选择当前栈或备用信号栈,根据SA_NODEFER/SA_RESETHAND/SA_RESTART调整后续行为。- 如果向用户栈写入
SignalFrame失败,handle_signal()会退化为CoreDump动作,而不是静默忽略。 check_signals()有possibly_has_signal快路径,避免每次用户态切换都扫描队列。
1.6 架构相关实现
src/arch/* 并不只是放常量,而是完成两件关键工作:
- 定义一页对齐的
signal_trampoline汇编入口,供用户处理器返回时触发rt_sigreturn。 - 定义每个架构的
UContext/MContext布局,把UserContext精确保存到用户栈。
当前实现中:
- RISC-V、AArch64、LoongArch64 的 trampoline 直接发起 syscall 号
139。 - x86_64 的 trampoline 发起 syscall 号
15。 starry-kernel::mm::loader::map_trampoline()会把这段 trampoline 映射到固定的SIGNAL_TRAMPOLINE地址。
1.7 与 starry-kernel 的边界
starry-signal 只返回“OS 应该做什么”,并不亲自修改任务状态机。比如:
SignalOSAction::Terminate/CoreDump/Stop/Continue的最终执行在starry-kernel::task::signal::check_signals()。- 当前 StarryOS 内核里
Stop仍被临时映射成do_exit(1, true),Continue也还是 TODO。 kill/tgkill/sigqueue的权限判断、waitpid退出码传播、页故障后是否转成SIGSEGV,都在内核层完成。
所以本 crate 定义的是信号语义模型,而不是完整的进程生命周期策略。
核心功能
功能概览
- 定义 Linux 风格信号编号、默认动作和掩码表示。
- 管理标准信号与实时信号的 pending 队列。
- 管理进程级共享
sigaction表。 - 管理线程级 blocked mask、altstack 和 handler frame。
- 提供跨架构
rt_sigreturntrampoline 与上下文恢复能力。