信号处理
🎯 信号处理概述
信号是Unix/Linux系统中进程间通信的重要机制,用于通知进程发生了特定事件。掌握信号处理对于编写健壮的系统软件至关重要。
📡 基础信号概念
常见信号类型
rust
use nix::sys::signal::Signal;
fn signal_overview() {
println!("=== 常见Unix信号 ===");
println!("SIGINT ({}): 中断信号 (Ctrl+C)", Signal::SIGINT as i32);
println!("SIGTERM ({}): 终止信号", Signal::SIGTERM as i32);
println!("SIGKILL ({}): 强制终止信号", Signal::SIGKILL as i32);
println!("SIGUSR1 ({}): 用户定义信号1", Signal::SIGUSR1 as i32);
println!("SIGUSR2 ({}): 用户定义信号2", Signal::SIGUSR2 as i32);
println!("SIGCHLD ({}): 子进程状态改变", Signal::SIGCHLD as i32);
println!("SIGPIPE ({}): 管道破裂", Signal::SIGPIPE as i32);
println!("SIGALRM ({}): 定时器信号", Signal::SIGALRM as i32);
}
🔧 信号处理实现
基础信号处理器
rust
use nix::sys::signal::{self, Signal, SigHandler};
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::Arc;
// 全局状态变量
static SHUTDOWN_REQUESTED: AtomicBool = AtomicBool::new(false);
static SIGNAL_COUNT: AtomicI32 = AtomicI32::new(0);
extern "C" fn handle_sigint(_signal: libc::c_int) {
let count = SIGNAL_COUNT.fetch_add(1, Ordering::Relaxed) + 1;
if count == 1 {
println!("\n收到SIGINT信号,准备优雅关闭... (再次按Ctrl+C强制退出)");
SHUTDOWN_REQUESTED.store(true, Ordering::Relaxed);
} else {
println!("\n收到第二次SIGINT信号,强制退出!");
std::process::exit(1);
}
}
extern "C" fn handle_sigusr1(_signal: libc::c_int) {
println!("收到SIGUSR1信号 - 执行自定义操作");
}
extern "C" fn handle_sigterm(_signal: libc::c_int) {
println!("收到SIGTERM信号 - 开始优雅关闭");
SHUTDOWN_REQUESTED.store(true, Ordering::Relaxed);
}
fn setup_signal_handlers() -> nix::Result<()> {
unsafe {
// 注册信号处理器
signal::signal(Signal::SIGINT, SigHandler::Handler(handle_sigint))?;
signal::signal(Signal::SIGUSR1, SigHandler::Handler(handle_sigusr1))?;
signal::signal(Signal::SIGTERM, SigHandler::Handler(handle_sigterm))?;
// 忽略SIGPIPE信号
signal::signal(Signal::SIGPIPE, SigHandler::SigIgn)?;
}
Ok(())
}
fn signal_handling_demo() -> nix::Result<()> {
setup_signal_handlers()?;
println!("信号处理演示程序启动");
println!("进程PID: {}", nix::unistd::getpid());
println!("发送信号测试:");
println!(" kill -INT {} (或按 Ctrl+C)", nix::unistd::getpid());
println!(" kill -USR1 {}", nix::unistd::getpid());
println!(" kill -TERM {}", nix::unistd::getpid());
let mut counter = 0;
while !SHUTDOWN_REQUESTED.load(Ordering::Relaxed) {
std::thread::sleep(std::time::Duration::from_secs(1));
counter += 1;
if counter % 5 == 0 {
println!("程序运行中... ({}秒)", counter);
}
}
println!("程序正在优雅关闭...");
std::thread::sleep(std::time::Duration::from_secs(1));
println!("清理完成,程序退出");
Ok(())
}
高级信号处理
rust
use nix::sys::signal::{sigaction, SigAction, SigSet, SaFlags};
use nix::sys::signalfd::{SignalFd, SfdFlags};
// 使用sigaction进行更精确的信号控制
fn advanced_signal_handling() -> nix::Result<()> {
// 创建信号集
let mut mask = SigSet::empty();
mask.add(Signal::SIGINT);
mask.add(Signal::SIGUSR1);
// 阻塞这些信号,通过signalfd处理
mask.thread_block()?;
// 创建signalfd
let mut sfd = SignalFd::new(&mask, SfdFlags::SFD_CLOEXEC)?;
println!("使用signalfd处理信号...");
println!("发送信号: kill -INT {} 或 kill -USR1 {}",
nix::unistd::getpid(), nix::unistd::getpid());
loop {
match sfd.read_signal() {
Ok(Some(signal)) => {
match signal.ssi_signo as i32 {
libc::SIGINT => {
println!("通过signalfd收到SIGINT");
break;
}
libc::SIGUSR1 => {
println!("通过signalfd收到SIGUSR1");
println!("信号发送者PID: {}", signal.ssi_pid);
}
_ => {
println!("收到其他信号: {}", signal.ssi_signo);
}
}
}
Ok(None) => {
// 没有信号,继续等待
continue;
}
Err(e) => {
eprintln!("读取信号失败: {}", e);
break;
}
}
}
Ok(())
}
⏰ 定时器和闹钟
使用alarm设置定时器
rust
use nix::sys::signal::{alarm, Signal, SigHandler};
use std::sync::atomic::{AtomicBool, Ordering};
static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
extern "C" fn handle_alarm(_signal: libc::c_int) {
println!("定时器触发!");
ALARM_TRIGGERED.store(true, Ordering::Relaxed);
}
fn alarm_example() -> nix::Result<()> {
unsafe {
signal::signal(Signal::SIGALRM, SigHandler::Handler(handle_alarm))?;
}
println!("设置5秒定时器...");
alarm::alarm(5);
// 等待定时器触发
while !ALARM_TRIGGERED.load(Ordering::Relaxed) {
std::thread::sleep(std::time::Duration::from_millis(100));
print!(".");
use std::io::{self, Write};
io::stdout().flush().unwrap();
}
println!("\n定时器完成!");
Ok(())
}
高精度定时器
rust
use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags};
use nix::sys::time::{TimeSpec, TimeValLike};
use std::os::unix::io::AsRawFd;
fn high_precision_timer() -> nix::Result<()> {
// 创建定时器
let timer_fd = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())?;
// 设置定时器:1秒后触发,然后每500毫秒重复
let initial = TimeSpec::from_duration(std::time::Duration::from_secs(1));
let interval = TimeSpec::from_duration(std::time::Duration::from_millis(500));
timer_fd.set(initial, Some(interval), TimerSetTimeFlags::empty())?;
println!("高精度定时器启动...");
let fd = timer_fd.as_raw_fd();
let mut count = 0;
loop {
// 等待定时器事件
let mut fds = [libc::pollfd {
fd,
events: libc::POLLIN,
revents: 0,
}];
unsafe {
let result = libc::poll(fds.as_mut_ptr(), 1, -1);
if result > 0 {
// 读取定时器事件
let mut buffer = [0u8; 8];
libc::read(fd, buffer.as_mut_ptr() as *mut libc::c_void, 8);
count += 1;
println!("定时器事件 #{}", count);
if count >= 10 {
break;
}
}
}
}
println!("定时器演示完成");
Ok(())
}
🔄 信号发送
发送信号给其他进程
rust
use nix::sys::signal::{kill, Signal};
use nix::unistd::Pid;
fn send_signals_example() -> nix::Result<()> {
let current_pid = nix::unistd::getpid();
println!("当前进程PID: {}", current_pid);
// 创建子进程用于演示
match unsafe { nix::unistd::fork() }? {
nix::unistd::ForkResult::Parent { child } => {
println!("父进程: 创建了子进程 {}", child);
// 等待一下让子进程设置信号处理器
std::thread::sleep(std::time::Duration::from_secs(1));
// 发送SIGUSR1信号给子进程
println!("父进程: 发送SIGUSR1给子进程");
kill(child, Signal::SIGUSR1)?;
std::thread::sleep(std::time::Duration::from_secs(1));
// 发送SIGTERM信号终止子进程
println!("父进程: 发送SIGTERM给子进程");
kill(child, Signal::SIGTERM)?;
// 等待子进程结束
nix::sys::wait::waitpid(child, None)?;
println!("父进程: 子进程已结束");
}
nix::unistd::ForkResult::Child => {
// 子进程设置信号处理器
unsafe {
signal::signal(Signal::SIGUSR1, SigHandler::Handler(|_| {
println!("子进程: 收到SIGUSR1信号");
}))?;
signal::signal(Signal::SIGTERM, SigHandler::Handler(|_| {
println!("子进程: 收到SIGTERM信号,准备退出");
std::process::exit(0);
}))?;
}
println!("子进程: 等待信号...");
// 子进程主循环
loop {
std::thread::sleep(std::time::Duration::from_millis(100));
}
}
}
Ok(())
}
🛡️ 信号安全编程
信号安全的数据结构
rust
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
use std::sync::Arc;
// 信号安全的计数器
struct SignalSafeCounter {
count: AtomicUsize,
max_count: usize,
}
impl SignalSafeCounter {
fn new(max_count: usize) -> Self {
SignalSafeCounter {
count: AtomicUsize::new(0),
max_count,
}
}
fn increment(&self) -> bool {
let current = self.count.fetch_add(1, Ordering::Relaxed);
current < self.max_count
}
fn get(&self) -> usize {
self.count.load(Ordering::Relaxed)
}
fn reset(&self) {
self.count.store(0, Ordering::Relaxed);
}
}
static SIGNAL_COUNTER: SignalSafeCounter = SignalSafeCounter {
count: AtomicUsize::new(0),
max_count: 5,
};
extern "C" fn safe_signal_handler(_signal: libc::c_int) {
if SIGNAL_COUNTER.increment() {
// 只使用信号安全的函数
unsafe {
let msg = b"Signal received\n";
libc::write(libc::STDERR_FILENO, msg.as_ptr() as *const libc::c_void, msg.len());
}
} else {
unsafe {
let msg = b"Too many signals, exiting\n";
libc::write(libc::STDERR_FILENO, msg.as_ptr() as *const libc::c_void, msg.len());
libc::_exit(1);
}
}
}
fn signal_safe_programming() -> nix::Result<()> {
unsafe {
signal::signal(Signal::SIGUSR1, SigHandler::Handler(safe_signal_handler))?;
}
println!("信号安全编程演示");
println!("发送信号: kill -USR1 {}", nix::unistd::getpid());
println!("最多处理5个信号");
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
let count = SIGNAL_COUNTER.get();
if count > 0 {
println!("已处理 {} 个信号", count);
}
if count >= 5 {
break;
}
}
Ok(())
}
自恢复信号处理
rust
use std::sync::atomic::{AtomicU32, Ordering};
static RESTART_COUNT: AtomicU32 = AtomicU32::new(0);
const MAX_RESTARTS: u32 = 3;
extern "C" fn restart_handler(_signal: libc::c_int) {
let count = RESTART_COUNT.fetch_add(1, Ordering::Relaxed);
if count < MAX_RESTARTS {
unsafe {
let msg = format!("Restarting... (attempt {})\n", count + 1);
libc::write(
libc::STDERR_FILENO,
msg.as_ptr() as *const libc::c_void,
msg.len()
);
}
// 这里可以执行重启逻辑
// 例如:重新初始化资源、重新连接数据库等
} else {
unsafe {
let msg = b"Max restart attempts reached, exiting\n";
libc::write(
libc::STDERR_FILENO,
msg.as_ptr() as *const libc::c_void,
msg.len()
);
libc::_exit(1);
}
}
}
fn self_healing_service() -> nix::Result<()> {
unsafe {
signal::signal(Signal::SIGUSR2, SigHandler::Handler(restart_handler))?;
}
println!("自恢复服务启动");
println!("发送重启信号: kill -USR2 {}", nix::unistd::getpid());
println!("最多允许{}次重启", MAX_RESTARTS);
let mut work_counter = 0;
loop {
// 模拟工作
std::thread::sleep(std::time::Duration::from_secs(2));
work_counter += 1;
println!("工作循环 #{}", work_counter);
let restart_count = RESTART_COUNT.load(Ordering::Relaxed);
if restart_count >= MAX_RESTARTS {
println!("达到最大重启次数,服务停止");
break;
}
}
Ok(())
}
📊 信号统计和监控
信号统计收集
rust
use std::collections::HashMap;
use std::sync::Mutex;
lazy_static::lazy_static! {
static ref SIGNAL_STATS: Mutex<HashMap<i32, u32>> = Mutex::new(HashMap::new());
}
extern "C" fn stats_signal_handler(signal: libc::c_int) {
if let Ok(mut stats) = SIGNAL_STATS.lock() {
*stats.entry(signal).or_insert(0) += 1;
}
}
fn signal_monitoring() -> nix::Result<()> {
// 注册多个信号的统计处理器
unsafe {
signal::signal(Signal::SIGUSR1, SigHandler::Handler(stats_signal_handler))?;
signal::signal(Signal::SIGUSR2, SigHandler::Handler(stats_signal_handler))?;
signal::signal(Signal::SIGINT, SigHandler::Handler(stats_signal_handler))?;
}
println!("信号监控启动");
println!("发送信号进行测试:");
println!(" kill -USR1 {}", nix::unistd::getpid());
println!(" kill -USR2 {}", nix::unistd::getpid());
println!(" kill -INT {} (Ctrl+C)", nix::unistd::getpid());
let mut last_report = std::time::Instant::now();
loop {
std::thread::sleep(std::time::Duration::from_millis(500));
// 每5秒报告一次统计
if last_report.elapsed() >= std::time::Duration::from_secs(5) {
if let Ok(stats) = SIGNAL_STATS.lock() {
if !stats.is_empty() {
println!("\n=== 信号统计 ===");
for (signal, count) in stats.iter() {
let signal_name = match *signal {
libc::SIGUSR1 => "SIGUSR1",
libc::SIGUSR2 => "SIGUSR2",
libc::SIGINT => "SIGINT",
_ => "UNKNOWN",
};
println!("{}: {} 次", signal_name, count);
}
println!("================\n");
}
}
last_report = std::time::Instant::now();
}
// 检查是否收到SIGINT
if let Ok(stats) = SIGNAL_STATS.lock() {
if stats.get(&(libc::SIGINT)).unwrap_or(&0) > &0 {
println!("收到SIGINT,退出监控");
break;
}
}
}
Ok(())
}
📚 最佳实践
信号处理最佳实践
rust
// 1. 使用信号安全的函数
const SIGNAL_SAFE_FUNCTIONS: &[&str] = &[
"write", "read", "open", "close",
"signal", "sigaction", "kill",
"_exit", "abort"
];
// 2. 避免在信号处理器中使用的函数
const UNSAFE_FUNCTIONS: &[&str] = &[
"malloc", "free", "printf", "fprintf",
"mutex_lock", "pthread_create"
];
// 3. 推荐的信号处理模式
fn recommended_signal_pattern() -> nix::Result<()> {
// 使用self-pipe技巧
let (read_fd, write_fd) = nix::unistd::pipe()?;
// 信号处理器只写入一个字节到管道
extern "C" fn simple_handler(_: libc::c_int) {
unsafe {
libc::write(WRITE_FD, b"1".as_ptr() as *const libc::c_void, 1);
}
}
static mut WRITE_FD: i32 = -1;
unsafe {
WRITE_FD = write_fd;
signal::signal(Signal::SIGUSR1, SigHandler::Handler(simple_handler))?;
}
println!("推荐的信号处理模式演示");
println!("发送信号: kill -USR1 {}", nix::unistd::getpid());
// 主循环使用select/poll监听管道
let mut buffer = [0u8; 1];
loop {
match nix::unistd::read(read_fd, &mut buffer) {
Ok(_) => {
println!("通过管道收到信号通知");
// 在这里安全地处理信号
break;
}
Err(nix::errno::Errno::EINTR) => {
// 被信号中断,继续
continue;
}
Err(e) => {
eprintln!("读取管道失败: {}", e);
break;
}
}
}
nix::unistd::close(read_fd)?;
nix::unistd::close(write_fd)?;
Ok(())
}
信号处理是Unix系统编程的重要技能,正确使用信号能让您的程序更加健壮和用户友好!📡