信号处理

未匹配的标注

信号处理

诸如命令行应用这样的进程需要对操作系统发送的信号作出反应。最常见的就比如 Ctrl+C,该信号通常告诉进程终止。要在 Rust 程序中处理信号,你需要考虑如何接收这些信号以及如何做出反应。

注意:
如果你的应用不需要优雅地关停,则使用默认处理方式就行(即立即退出,让操作系统清理资源,如打开文件句柄)。该情况下:无需执行本章节告诉你的内容。

然而,对于那些需要自行清理的应用程序,本章节是很重要的!例如,如果你的应用程序需要正确地关闭网络连接(也就是对另一端的进程说 “再见”),删除临时文件,或者重置系统设置,还请继续阅读。

操作系统间差异

在 Unix 类系统( 例如 Linux, macOS, 和 FreeBSD )上,进程可以接收 信号。它可以以默认方式(操作系统所提供)对它们做出反应,捕获信号并以程序所定义的方式对它们进行处理,或者完全忽略信号。

Windows 没有信号,你可以用 Console Handlers 来定义在事件发生时执行的回调。还有 结构化异常处理,可以处理各种类型的系统异常,例如被 0 除,无效访问异常,栈溢出,等等之类的。

首先:处理 Ctrl+C

ctrlc crate 的用途恰如其名:其允许你以跨平台的方式对用户按的 Ctrl+C 做出反应,使用该 crate 的主要方法是:

use std::{thread, time::Duration};

fn main() {
    ctrlc::set_handler(move || {
        println!("received Ctrl+C!");
    })
    .expect("Error setting Ctrl-C handler");

    // Following code does the actual work, and can be interrupted by pressing
    // Ctrl-C. As an example: Let's wait a few seconds.
    thread::sleep(Duration::from_secs(2));
}

当然,这并无帮助:只打印消息,但是不会停止程序(译者注:除非出现错误)。

在实际的程序中,最好是在这个执行信号处理的程序中设置一个变量,然后在程序的各个位置进行检查。例如,可以在信号处理中设置一个 Arc<AtomicBool> ,然后在热循环(hot loops)中,或者当等待一个线程时,你定时地检查它,并在当它变为 true 时中断(break)。

处理其它类型的信号

ctrlc crate 仅处理 Ctrl+C ,或者,在 Unix 系统中被称为 SIGINT (“中断” 信号)。为了对更多的 Unix 信号做出反应,你应该查看 signal-hook这篇博文描述了其设计,它时目前社区所支持的最广泛的库。

这有个简单的例子:

use signal_hook::{iterator::Signals, SIGINT};
use std::{error::Error, thread, time::Duration};

fn main() -> Result<(), Box<dyn Error>> {
    let signals = Signals::new(&[SIGINT])?;

    thread::spawn(move || {
        for sig in signals.forever() {
            println!("Received signal {:?}", sig);
        }
    });

    // Following code does the actual work, and can be interrupted by pressing
    // Ctrl-C. As an example: Let's wait a few seconds.
    thread::sleep(Duration::from_secs(2));

    Ok(())
}

使用 channel

不设置变量并使用程序的其他部分检查它,你可以使用 通道(channel) :创建一个通道,每当接收到信号时,信号处理程序就向该通道发出一个值。在你的应用程序代码中,可以使用这个通道和其他通道作为线程之间的同步点,使用 crossbeam-channel 看起来像这样:

use std::time::Duration;
use crossbeam_channel::{bounded, tick, Receiver, select};
use anyhow::Result;

fn ctrl_channel() -> Result<Receiver<()>, ctrlc::Error> {
    let (sender, receiver) = bounded(100);
    ctrlc::set_handler(move || {
        let _ = sender.send(());
    })?;

    Ok(receiver)
}

fn main() -> Result<()> {
    let ctrl_c_events = ctrl_channel()?;
    let ticks = tick(Duration::from_secs(1));

    loop {
        select! {
            recv(ticks) -> _ => {
                println!("working!");
            }
            recv(ctrl_c_events) -> _ => {
                println!();
                println!("Goodbye!");
                break;
            }
        }
    }

    Ok(())
}

使用 future 和 stream

如果你使用 tokio,说明你很可能已经在你的应用程序中使用了异步模式和事件驱动设计。相比直接使用 crossbeam 的通道,你可以使用 signal-hook 的 tokio-support feature,其允许你在 signal-hook 的类型上调用 .into_async() 来获得实现了 futures::Stream 的新类型。

当你正在处理第一个 Ctrl+C 的时候接收到了其他的 Ctrl+C 时怎么办

大多数用户会按 Ctrl+C,然后给你的程序几秒钟时间退出,或者告诉他们发生了什么。如果这并未发生,他们就会再次按下 Ctrl+C 。当然,最典型的行为是使应用程序立即退出。

(译者注:以下为多余部分,我也不知为何会多,提交的原文本来没有的)
诸如命令行应用这样的进程需要对操作系统发送的信号作出反应。最常见的就比如 Ctrl+C,该信号通常告诉进程终止。要在 Rust 程序中处理信号,你需要考虑如何接收这些信号以及如何做出反应。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/rust-cla-2020/s...

译文地址:https://learnku.com/docs/rust-cla-2020/s...

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~