← 返回信息流
AI 资讯Hacker News·3 小时前

Sigwire:Linux 信号实时切换终端工具

原标题:Show HN: Sigwire – a live TUI switchboard for every signal on your Linux box

速览

Sigwire 是一个运行在终端中的实时信号交换机(TUI),用于捕获和转发 Linux 系统上的各种信号。它提供可视化的界面让用户轻松管理进程信号,适用于调试和系统监控场景。该工具开源免费,面向开发者。

AI 深度解读

背景

在 Linux 系统中,信号(signal)是进程间通信和内核通知的基本机制。无论是用户按 Ctrl-C 发送 SIGINT,还是内核因段错误发送 SIGSEGV,抑或是 systemd 发送 SIGTERM 终止服务,信号无处不在。然而,调试信号相关的问题一直非常困难:传统工具如 strace 只能跟踪单个进程,ptrace 会引入性能开销且被跟踪进程能感知到,而系统级信号流(谁发送、谁接收、是否被捕获、是否中断了阻塞系统调用)几乎不可见。

Sigwire 正是为了解决这一痛点而生——它通过挂钩内核信号跟踪点(tracepoints),将整个系统的信号活动实时流式传输到终端,形成一个动态的“信号配线盘”(patchbay),让开发者能像 tail -f 一样观察所有信号。

核心内容

Sigwire 是一个运行在终端中的实时 TUI(文本用户界面)工具,它利用 Linux 内核的 BPF(Berkeley Packet Filter)机制,挂钩 signal:signal_generatesignal:signal_deliver 两个跟踪点,以及 rt_sigreturn(2) 和系统调用退出跟踪点,捕获每个信号从生成到传递的完整生命周期。与 strace -fptrace 不同,Sigwire 不绑定任何进程,被跟踪的进程完全不知情,且无额外 ptrace 开销。

安装与运行

一次安装守护进程(yeet daemon):

curl -fsSL https://yeet.cx | sh   # 安装 yeet daemon(一次性)
yeet run github:yeet-src/sigwire   # 运行仪表盘(守护进程负责加载有特权的 BPF 程序)

仅支持 Linux。无需任何配置——系统上恒有信号流量,启动后立即开始滚动。

界面与交互

默认视图按时间倒序显示最新信号,每一行代表一个生成的信号,格式如下:

| 时间 | 发送方 | 信号 | 接收方 | 备注 | |------|--------|------|--------|------| | now | bash·4402──SIGINT───▶ | node·8813 | kill(2) ↯ EINTR read caught 41µs | | 1.2s | systemd·1──────SIGTERM──▶ | nginx·1291 | kill(2) caught 1.2ms | | 3.4s | kernel·8813──SIGSEGV──▶ | chrome·8813 | fault default ☠ | | 4.1s | postgres·507──SIGUSR1───▶ | postgres·509 ×6 | kill(2) caught 9µs |

  • 发送方/接收方:格式为 comm·pid,发送方是生成信号的主体(当前进程),接收方是目标进程。
  • 信号线:信号名称按严重性着色(256 色),颜色映射与 UI 其余部分一致。
  • ×N:当同一信号在短时间内被重复发送时折叠为一行,显示倍数。
  • 备注:包含信号来源(kill(2)tgkillsigqueuetimerkernelfault)以及是否中断了阻塞系统调用(↯ EINTR read,或 ↺ restarted read 若设置了 SA_RESTART),然后是处置方式:
    • caught 41µs:用户态处理程序运行了,并显示耗时。
    • default:无处理程序,执行默认动作。
    • ⊘ ignored:被忽略。
  • :标记真正的致命信号(见下方致命计数规则)。

每一行在传递结果确定后冻结,永不改变,因此突发信号流稳定滚动,不会闪烁。

右侧边栏(聚合视图)

默认显示按流量排序的顶部信号、来源分类、传递统计(捕获 vs 默认 vs 忽略)。按 /(或 p)冻结滚动并选中一行时,边栏变为详情面板,显示该信号的所有已知信息:

  • 信号编号与名称
  • 发送方/接收方详情
  • 生成方式(tgkillSI_TKILL、作用域等)
  • 传递结果(handled caughtdefault 等)
  • 系统调用中断详情(如 EINTR ← read
  • 处理程序地址(如 0x55f0a1c3)及运行时长(ran 3.0ms
  • sigaction 标志(SA_SIGINFOSA_RESTARTSA_NODEFER 等)
  • 目标进程在传递时刻的信号屏蔽字(sigprocmask

Esc 关闭详情,按 p 恢复实时滚动。

信号过滤(静音功能)

三个信号被认为是背景噪音:SIGCHLD(子进程回收)、SIGURG(Go 的异步抢占心跳)、SIGWINCH(终端窗口大小变化,广播给所有前台进程)。Sigwire 默认将它们静音,但用户可以自定义。

s 打开信号选择器:模态列表显示每个信号及其当前严重性颜色和已见数量,每个信号可切换为“显示”或“静音”。用方向键选择(或直接输入数字跳转,如 15 跳到 15),按空格切换。按 a 全选/全取消。标题栏会显示当前静音数量。

静音掩码是一个 __u64 全局变量,位于运行中的 BPF 程序的 .data 段内。切换时通过 DataSec.patch() 即时修改对应位,内核在信号到达环形缓冲区之前就将其丢弃,因此静音操作零成本,且无需重新加载程序。

致命信号计数规则

Sigwire 的 ☠ fatal 计数器非常保守。因为 signal_generate 在生成时触发,Sigwire 此时无法知道目标是否已安装处理程序——一个 SIGTERM 可能被捕获并优雅关闭,也可能被完全忽略。因此只有两种情况才计为死亡:

  1. SIGKILL 被传递——不可捕获、不可忽略,绝对致命;
  2. 内核自身引发的同步故障信号(SIGSEGVSIGBUSSIGABRTSIGILLSIGFPESIGTRAPSIGSYSSIGQUIT)——非用户态 kill 发起的信号。

其余所有信号(如 systemd 的 SIGTERM、Ctrl-C 的 SIGINT、运行时的 SIGPWR)只显示和着色,但不计为死亡,因为很可能不是。

核心架构

源码由两个 BPF C 文件(`src/bpf/s

查看原文 →github.com