信号处理的安全全局状态
Safe global state for signal handling
我正在研究 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
crate)需要指向 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<_>
指针。这可能意味着使用全局状态。
所以问题是:我应该为全局状态使用什么数据结构,它可以是 "unset"(即没有 signals
向量)或原子地 "set" 到一些可变状态我在 try_create
?
中初始化
对于这种类型的全局状态,我建议使用 lazy_static crate。您可以使用宏来定义延迟计算的可变全局引用。您也许可以使用全局 Option<T>
变量。
虽然这是这种情况的一个问题。你会 运行 遇到的一个大问题是很难只在信号处理程序内部做你想做的事情。由于信号处理程序必须是可重入的,因此任何类型的锁以及任何内存分配都不能使用(除非使用的内存分配器也是可重入的)。这意味着 Arc<Mutex<Vec<T>>>
类型或类似的东西将不起作用。不过,您可能已经知道并正在以某种方式处理它。
根据您的需要,我可能会向您指出 chan_signal crate,它是对信号的抽象,它使用线程和 sigwait
系统调用来接收信号。
希望对您有所帮助,另一个值得关注的有趣资源是 signalfd 函数,它创建一个文件描述符来对信号进行排队。 nix
crate 也绑定了它。
我正在研究 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
crate)需要指向 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<_>
指针。这可能意味着使用全局状态。
所以问题是:我应该为全局状态使用什么数据结构,它可以是 "unset"(即没有 signals
向量)或原子地 "set" 到一些可变状态我在 try_create
?
对于这种类型的全局状态,我建议使用 lazy_static crate。您可以使用宏来定义延迟计算的可变全局引用。您也许可以使用全局 Option<T>
变量。
虽然这是这种情况的一个问题。你会 运行 遇到的一个大问题是很难只在信号处理程序内部做你想做的事情。由于信号处理程序必须是可重入的,因此任何类型的锁以及任何内存分配都不能使用(除非使用的内存分配器也是可重入的)。这意味着 Arc<Mutex<Vec<T>>>
类型或类似的东西将不起作用。不过,您可能已经知道并正在以某种方式处理它。
根据您的需要,我可能会向您指出 chan_signal crate,它是对信号的抽象,它使用线程和 sigwait
系统调用来接收信号。
希望对您有所帮助,另一个值得关注的有趣资源是 signalfd 函数,它创建一个文件描述符来对信号进行排队。 nix
crate 也绑定了它。