WSL C# API 中的 Signal 枚举:从 WinRT 接口到底层 Linux 信号传递的完整解析
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
导读
Signal枚举是 Microsoft.WSL.Containers(WSLC)C#/WinRT 编程接口中用于表示 Linux 进程信号的类型,它在停止容器(Container.Stop)、向容器内进程发送信号(Process.Signal)以及解析进程崩溃信息(ProcessCrashInformation.Signal)等场景中承担着关键角色。本文以 signal.md 为骨架,结合 WSL 仓库中的 IDL 定义、原生 C ABI 映射、容器会话层实现与 CLI 测试用例,完整讲解每个枚举成员的含义、取值约束、底层调用链与使用注意事项,帮助你正确地在托管代码中控制 WSL 容器与进程的生命周期。
Signal 枚举定义与成员语义
Signal枚举定义于 wslcsdk.idl,通过 C#/WinRT 投影为托管枚举,完整定义如下:
public enum Signal { None = 0, SIGHUP = 1, SIGINT = 2, SIGQUIT = 3, SIGKILL = 9, SIGTERM = 15 }该枚举的数值直接对应 POSIX/Linux 的标准信号编号,各成员在 IDL 源注释中的定位如下:
| 成员 | 数值 | IDL 注释定位 | Linux 语义 |
|---|---|---|---|
None | 0 | No signal; reserved for future use | 无信号,为后续扩展保留,不应作为实际发送目标 |
SIGHUP | 1 | reload / hangup | 终端挂断或控制进程退出,守护进程通常用它触发配置重载 |
SIGINT | 2 | interrupt (Ctrl-C) | 键盘中断,即终端按下 Ctrl-C 产生的信号 |
SIGQUIT | 3 | quit with core dump | 退出并生成核心转储(core dump) |
SIGKILL | 9 | immediate termination | 强制立即终止,进程无法捕获、阻塞或忽略 |
SIGTERM | 15 | graceful shutdown | 优雅终止,进程可以捕获后自行清理并退出 |
依据:以上成员含义与注释直接来自 wslcsdk.idl。当前枚举仅覆盖这 6 个常用信号,原生头文件中明确标注 "Will define more signals as needed"(wslcsdk.h),即未来可按需扩展,使用时应以当前仓库版本为准。
Signal 在 WinRT API 中的三个典型使用场景
1. 停止容器:Container.Stop
// 原型(来自 wslcsdk.idl) void Stop(Signal signal, Windows.Foundation.TimeSpan timeout);调用Stop时,指定信号决定容器内的 init 进程以何种方式被终止,timeout决定等待其退出(优雅)的最长时间。实现位于 Container.cpp:
- 先把
TimeSpan换算为秒(duration_cast<seconds>); - 对超时做两层校验:超过
uint32_t上限抛hresult_invalid_argument("Timeout is too large"),为负值抛hresult_invalid_argument("Timeout must be non-negative"); - 最终将枚举强转为其原生 ABI 等价类型
WslcSignal,调用WslcStopContainer(container, signal, timeoutSeconds, errorMessage)。
这意味着:传SIGTERM并给出合理超时,可实现“先优雅停机、超时后由底层兜底强杀”的标准容器停止流程;而直接传SIGKILL则会立即强制终止。
2. 向进程发送信号:Process.Signal
// 原型(来自 wslcsdk.idl) void Signal(Signal signal);用于向已启动的容器进程发送任意支持的信号,实现位于 Process.cpp:
void Process::Signal(winrt::Microsoft::WSL::Containers::Signal const& signal) { winrt::check_hresult(WslcSignalProcess(ToHandle(), static_cast<WslcSignal>(signal))); }典型用途包括:向服务进程发送SIGHUP触发配置热重载、发送SIGINT模拟 Ctrl-C 中断、发送SIGTERM请求优雅退出,或发送SIGKILL强制清理异常进程。
3. 读取崩溃信号:ProcessCrashInformation.Signal
当进程因信号终止时,ProcessCrashHandler事件会携带 ProcessCrashInformation 对象,其Signal属性(UInt32,见 wslcsdk.idl)返回导致崩溃的信号编号(如11对应 SIGSEGV)。该值并非Signal枚举类型,而是原始数字,因此请勿用本枚举做直接强转,应将其作为诊断信息读取;实现见 ProcessCrashInformation.cpp。
底层映射:从 C# 枚举到原生 WslcSignal
托管枚举并非凭空而来,它严格对齐了 WinRT 层的 C++ 原生枚举与 C ABI 层的 C 枚举。在 wslcsdk.h 中,C 接口定义了与之逐值对应的WslcSignal:
typedef enum WslcSignal { WSLC_SIGNAL_NONE = 0, // No signal; reserved for future use WSLC_SIGNAL_SIGHUP = 1, // SIGHUP: reload / hangup WSLC_SIGNAL_SIGINT = 2, // SIGINT: interrupt (Ctrl-C) WSLC_SIGNAL_SIGQUIT = 3, // SIGQUIT: quit with core dump WSLC_SIGNAL_SIGKILL = 9, // SIGKILL: immediate termination WSLC_SIGNAL_SIGTERM = 15, // SIGTERM: graceful shutdown } WslcSignal;调用链清晰可见:C# 枚举 → WinRT 枚举(winrt::Microsoft::WSL::Containers::Signal)→static_cast到 C ABI 枚举(WslcSignal)→ 进入 SDK 导出函数(wslcsdk.def)→ 由容器会话层最终传递给 Linux 侧进程。由于两层枚举数值完全一致,跨边界转换只是类型层面的重新解释,不会发生数值漂移。
在容器会话层的网络路径中,该信号值还会被直接序列化为 Docker Engine API 的signal查询参数,见 DockerHTTPClient.cpp:StopContainer与SignalContainer都会把WSLCSignal强转为整型后拼入/containers/{id}/stop与/containers/{id}/kill请求。这说明同一个信号语义在原生 SDK、Docker 兼容协议两条通道上保持一致。
CLI 层的信号解析:字符串与枚举互转
除编程 API 外,WSLC 的命令行工具同样支持以字符串指定信号,其解析逻辑与Signal枚举的取值一一对应,并由单元测试锁定行为。在 WSLCCLIArgumentUnitTests.cpp 中可看到以下规则:
- 支持
SIGTERM、SIGKILL、SIGHUP等带SIG前缀的形式; - 支持省略前缀的
TERM、HUP、KILL; - 匹配大小写不敏感(
sIgTerm、term均合法); - 支持数字形式,如
"15"解析为 SIGTERM; - 越界值(如
999)与未知名称(如INVALID_SIGNAL)会抛出ArgumentException。
测试还验证了Signal与StopSignal两个参数共用同一转换器(WSLCCLIArgumentUnitTests.cpp),并支持一次解析多个信号值后按序缓存(WSLCCLIArgumentUnitTests.cpp)。
使用建议与注意事项
- 停止容器优先使用 SIGTERM + 超时:
Container.Stop携带超时参数,允许容器优雅关闭;SIGKILL应仅用于无响应场景,因其不可被捕获,会跳过所有清理逻辑。 - 区分三种用途的类型差异:
Container.Stop与Process.Signal接收Signal枚举;ProcessCrashInformation.Signal返回原始UInt32信号编号,仅作诊断,不要误当作枚举使用。 None仅为占位:IDL 注释明确其为 "reserved for future use",不建议作为实际发送信号;需要“不指定信号”的语义时,在 CLI 层选择省略对应参数,而不是显式传None。- 超时参数有边界约束:
Container.Stop的TimeSpan会换算为秒并校验非负且不超过uint32_t上限,超大或负值会直接抛异常(见 Container.cpp)。 - 枚举扩展遵循 ABI 对齐:新增信号时需同时更新 IDL 枚举与 C ABI 枚举并保持数值一致,托管层与原生层才能继续安全互转。
延伸阅读
- Signal 枚举参考页(本文主体)
- 其他枚举参考:如
ProcessState、ContainerState、DeleteContainerOption等配套类型 - Container 核心类 与 Process 核心类:枚举的实际消费方
- ProcessCrashInformation 数据类:崩溃信号读取
- 原生定义:wslcsdk.idl、wslcsdk.h
- 会话层 Docker 协议映射:DockerHTTPClient.cpp
- CLI 信号解析测试:WSLCCLIArgumentUnitTests.cpp
【免费下载链接】WSLWindows Subsystem for Linux项目地址: https://gitcode.com/GitHub_Trending/ws/WSL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考