综述:
使用过Unix/Linux类型系统的朋友都应该比较熟悉“信号”是个什么东西,列如:ctrl + c用于终止终端shell运行的程序, 网络服务器进程还可能接收到诸如:SIGPIPE/SIGCHLD等信号, 操作系统通过信号通知程序远端连接不通了,或者是子进程退出了等等,需要我们的程序应对处理。实则Windows下也支持ctrl+c , 只是Windows平台下信号机制与Linux平台不同。本笔记以Linux为主。
信号捕获和处理涉及到进程间通讯,进程优雅退出,系统调用(如:读写操作)的一些异常通知等等, 所以在Linux编写程序,则必须思考信号捕获处理的问题, 不可轻视而忽略之。
针对信号的捕获和处理, 下面给出一个Rust实现小例子,抛砖引玉,仅供大家参考, 当然实现信号捕获和处理的方法许多,列如:通过libc crate直接调用操作系统的接口来捕获和处理信号 ; 当然也可以采用其他第三方库来简化信号的捕获和处理,signal-hook仅仅是其中一个,我们也可以去https://lib.rs/和crates.io上去搜索适合的库。
Rust代码例子(信号捕获处理和优雅退出)
//Cargo.toml
[package]
name = "signal_test"
version = "0.1.0"
edition = "2024"
[dependencies]
mio = "1.1.0" # 锁定稳定版本
signal-hook = "0.3.18" # 锁定稳定版本
#signal-hook-mio = "0.2.5" # 关键桥接库,与前两者版本匹配
signal-hook-mio = {version ="0.2.5", features = ["support-v1_0"]}
tracing = "0.1.43"
tracing-subscriber = "0.3.22"
//main.rs
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use mio::{Events, Interest, Poll, Token};
use signal_hook::{consts::SIGINT, consts::SIGTERM};
//use signal_hook_mio::Signals;
use signal_hook_mio::v1_0::Signals;
use tracing::{info, warn};
const TOKEN_SIGNAL: Token = Token(0);
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化日志(确保能看到关键输出)
tracing_subscriber::fmt::init();
info!("最小信号捕获Demo启动,按 Ctrl+C 测试...");
// 1. 创建Poll(核心,确保无异常)
let mut poll = Poll::new()?;
info!("Poll创建成功");
// 2. 初始化信号监听(仅捕获SIGINT/Ctrl+C、SIGTERM)
let mut signals = match Signals::new(&[SIGINT, SIGTERM]) {
Ok(s) => {
info!("信号监听初始化成功,监听信号:SIGINT(2)、SIGTERM(15)");
s
}
Err(e) => {
warn!("信号监听初始化失败:{}", e);
return Err(e.into());
}
};
// 3. 注册信号FD到Poll(关键步骤,打印日志确认)
match poll.registry().register(
&mut signals,
TOKEN_SIGNAL,
Interest::READABLE, // 信号触发时FD变为可读,必须监听READABLE
) {
Ok(_) => info!("信号FD注册到Poll成功,Token={:?}", TOKEN_SIGNAL),
Err(e) => {
warn!("信号FD注册失败:{}", e);
return Err(e.into());
}
}
// 4. 优雅退出标记(线程安全,避免static mut隐患)
let shutdown_flag = Arc::new(AtomicBool::new(false));
let shutdown_flag_clone = shutdown_flag.clone();
// 5. Poll事件循环(核心,必须有超时,避免无限阻塞)
let mut events = Events::with_capacity(16);
loop {
// 检查退出标记,触发则退出
if shutdown_flag.load(Ordering::Relaxed) {
info!("捕获到退出信号,开始优雅退出...");
std::thread::sleep(Duration::from_secs(1)); // 模拟清理资源
info!("优雅退出完成,程序终止");
break;
}
// 关键:设置500ms超时,避免Poll永久阻塞(无事件时也会定期唤醒检查退出标记)
info!("Poll等待事件中...(超时50000ms)");
match poll.poll(&mut events, Some(Duration::from_millis(50000))) {
Ok(_) => info!("Poll唤醒,触发事件数:{:?}", events),
Err(e) => {
warn!("Poll失败:{}", e);
continue;
}
}
// 遍历处理所有事件
for event in events.iter() {
info!("触发事件:Token={:?}, Interest={:?}", event.token(), event);
match event.token() {
TOKEN_SIGNAL => {
info!("处理信号事件,读取触发的信号...");
// 必须循环读取所有待处理信号(避免漏处理)
for signal in signals.pending() {
match signal {
SIGINT => {
info!("✅ 成功捕获 Ctrl+C 信号(SIGINT,编号2)");
shutdown_flag_clone.store(true, Ordering::Relaxed);
}
SIGTERM => {
info!("✅ 成功捕获 SIGTERM 信号(编号15)");
shutdown_flag_clone.store(true, Ordering::Relaxed);
}
other => warn!("捕获到未知信号:编号{}", other),
}
}
}
_ => warn!("未知事件Token:{:?}", event.token()),
}
}
}
Ok(())
}
代码编译和执行环境: deepin linux home v25 rust 2024 1.91.1 stable.
注意: 可以通过如下命令列出Linux支持的信号集合:
# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
理解信号捕获和处理是Linux下编程的基础, 需要我们认真对待。
后记:
我是一个普通的c++老码农,Rust语言爱好者,如今已四十不惑,青丝白发生,人谈不上机智,然多年来敏而好学,不敢懈怠, 随处学随处记,笔耕不辍,坚信好脑瓜不如烂笔头!如今赋闲家中,翻腾出来这些粗鄙之文,分享出来,抛砖引玉!不过本人水平有限,许多时候也是不求甚解,匆促行文,故而文中难免存有谬误,望诸君海涵指正!
帮忙点个赞吧!鼓励我坚持写下去!谢谢啦!