信号处理的安全全局状态

时间:2015-11-01 21:36:58

标签: linux concurrency signals rust posix

我正在玩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中初始化的一些可变状态}}?

1 个答案:

答案 0 :(得分:3)

对于这种类型的全局状态,我建议使用lazy_static包。您可以使用宏来定义延迟评估的可变全局引用。您可以使用全局Option<T>变量获得一种方法。

但这是这种情况的一个问题。您将遇到的一个大问题是,仅在信号处理程序内部执行您想要的操作很困难。由于信号处理程序必须是可重入的,因此任何类型的锁都会出现以及任何内存分配(除非使用的内存分配器也是可重入的)。这意味着Arc<Mutex<Vec<T>>>类型或类似的东西不起作用。你可能已经知道并且正在以某种方式处理它。

根据您的需要,我可能会指向chan_signal crate,这是对使用线程和sigwait系统调用来接收信号的信号的抽象。

希望有所帮助,另一个值得关注的有趣资源是signalfd函数,它创建一个文件描述符来排列信号。 nix箱子也具有约束力。