我正在玩Rust和各种UNIX库。我现在使用的一个用例是我想对POSIX信号作出反应。为了保持合理性,我想在信号处理上创建一个抽象,以便我的程序的其余部分不必担心它们。
让我们调用抽象SignalHandler
:
struct SignalHandler {
pub signals: Arc<Vec<libc::c_int>>,
}
我希望这个signals
向量填充所有收到的信号。我的真实状态更复杂,但我们以此向量为例。
我希望API的行为如下:
// ← No signals are being captured
let Some(h) = SignalHandler::try_create();
// ← Signals are added to h.signals
// Only one signal handler can be active at a time per process
assert_eq!(None, SignalHandler::try_create());
// ← Signals are added to h.signals
drop(h);
// ← No signals are being captured
问题是注册信号处理程序(例如使用nix
包)需要指向C函数的指针:
use nix::sys::signal;
let action = signal::SigAction::new(handle_signal, signal::SockFlag::empty(), signal::SigSet::empty());
signal::sigaction(signal::SIGINT, &action);
我无法将signals
向量传递给handle_signal
函数,因为它需要具有C ABI,因此不能成为闭包。我想以某种方式给出一个Weak<_>
指向该函数的指针。这可能意味着使用全局状态。
所以问题是:我应该使用什么数据结构用于全局状态,可以“取消设置”(即没有signals
向量)或原子地“设置”为我在{{1中初始化的一些可变状态}}?
答案 0 :(得分:3)
对于这种类型的全局状态,我建议使用lazy_static包。您可以使用宏来定义延迟评估的可变全局引用。您可以使用全局Option<T>
变量获得一种方法。
但这是这种情况的一个问题。您将遇到的一个大问题是,仅在信号处理程序内部执行您想要的操作很困难。由于信号处理程序必须是可重入的,因此任何类型的锁都会出现以及任何内存分配(除非使用的内存分配器也是可重入的)。这意味着Arc<Mutex<Vec<T>>>
类型或类似的东西不起作用。你可能已经知道并且正在以某种方式处理它。
根据您的需要,我可能会指向chan_signal crate,这是对使用线程和sigwait
系统调用来接收信号的信号的抽象。
希望有所帮助,另一个值得关注的有趣资源是signalfd函数,它创建一个文件描述符来排列信号。 nix
箱子也具有约束力。