1 个不稳定版本
新 0.1.0 | 2024年8月18日 |
---|
#233 in Unix API
61KB
754 行
signals_receipts
一种简单、轻量、异步信号安全且可移植的POSIX信号处理方法。
每个感兴趣的信号编号都与以下内容关联:自上次检查以来该信号被发送的原子计数器,以及该信号的定制处理。当任何感兴趣的信号被发送时,都会发布一个信号量,唤醒一个检查所有计数器并将任务委托给您的自定义处理的消费者线程(该处理在正常上下文中运行,而不是在信号处理程序的中断上下文中,这会极大地限制只能执行异步信号安全操作)。
示例
// This defines the `signals_receipts_premade` module.
signals_receipts::premade! {
SIGINT => |receipt| println!("Interrupted {} times since last.", receipt.cur_count);
SIGTERM => |control| control.break_loop();
}
fn main() {
use crate::signals_receipts_premade::SignalsReceipts;
use signals_receipts::Premade as _;
SignalsReceipts::install_all_handlers();
let consumer = std::thread::spawn(SignalsReceipts::consume_loop);
consumer.join();
println!("Terminated.");
}
动机
该包仅适用于POSIX操作系统,当只需要计数器和每个信号编号的单个代理时。不适用于需要通过SA_SIGINFO
提供额外信息的场景。不适用于需要每个信号编号多个代理的场景(尽管,您可以使用这个包创建类似的东西)。不适用于支持Windows。拥有这些功能将使该包过于复杂。
使用POSIX信号量,这是该包所使用的,比经典的“自管道技巧”(即signal_hook
包用于其迭代器的技巧)更简单、更干净,用于从极端受限的信号处理程序中唤醒消费者线程。
另一个经典的方法是使用消费者线程中的sigwait
(或其变体),以避免使用异步信号处理程序,但这种方法有时不太可取,因为它需要将所有线程中的所有信号都屏蔽掉,这会干扰所有子进程(即子进程通过exec
继承父进程的所有屏蔽信号掩码,这可能会破坏它们的程序,除非仔细重置每个)。该包适用于不使用这种方法的情况,即适用于使用异步信号处理程序的情况。
这个包公开了一些机制,以便您可以根据自己的需求对其进行自定义,使其与该包的预设方法有所不同。
替代方案
signal_hook
包提供了一组令人印象深刻的特性,尽管它在异步信号安全方面受到限制。它提供的信号迭代器简单易用,可以跨线程初始化和使用,有时仅使用它就足够了。它可以提供 SA_SIGINFO
的额外信息。不使用 signal_hook
的情况是,如果其完整功能集大部分未被使用,那么这将是过度设计。如果您不确定是否会过度设计,您可能希望选择 signal_hook
。
可移植性
该包已经在 (x86_64) 上通过构建和测试确认。
- BSD
- FreeBSD 14.0
- NetBSD 9.1
- Linux
- Alpine 3.18 (使用 musl)
- Debian 12
- NixOS 24.05
- Ubuntu 23.10
- Solaris
- OpenIndiana 2024.04
它可能在其他 POSIX 操作系统上已经工作。如果不工作,为其他 POSIX 操作系统添加支持应该很简单,但可能需要对此包的条件编译进行一些调整。
不支持 macOS
不幸的是,macOS 不提供未命名的信号量 API(违反了现代 POSIX 版本要求它),因此 sem_safe
依赖项在 macOS 上无法工作。代替使用它,可能可以使用 macOS 的某些 API 来实现相同的功能,并且是异步信号安全的。
依赖项
~0–7MB
~36K SLoC