#子进程 #进程 #杀死 #终止 #CLI工具 #taskkill #tree-kill

kill_tree

🌳 Kill Tree是一个库,用于递归地终止指定的进程及其所有子进程,独立于其他命令(如kill或taskkill)运行。

10个版本

0.2.4 2024年2月12日
0.2.3 2024年2月12日
0.1.5 2024年2月7日

#123操作系统

Download history 27/week @ 2024-03-14 1/week @ 2024-03-21 12/week @ 2024-03-28 58/week @ 2024-04-04 89/week @ 2024-04-11 124/week @ 2024-04-18 60/week @ 2024-04-25 97/week @ 2024-05-02 105/week @ 2024-05-09 140/week @ 2024-05-16 46/week @ 2024-05-23 155/week @ 2024-05-30 210/week @ 2024-06-06 183/week @ 2024-06-13 391/week @ 2024-06-20 1925/week @ 2024-06-27

2,726 每月下载量
用于 3 个crate(2个直接)

MIT 许可证

87KB
2K SLoC

Kill Tree

logo

🌳 Kill Tree是一个库和CLI工具,用于递归地终止指定的进程及其所有子进程,独立于其他命令(如kill或taskkill)运行。
它是用Rust编写的。
如果您将其用作Rust库,它支持同步和异步实现!
异步实现由Tokio提供。
本项目受到node-tree-kill的启发。谢谢。🤟

如果您能star(⭐)这个仓库,我将非常感激! 点击跳转到仓库。

Build Status Crates.io

为什么创建这个项目

TL;DR


我创建这个项目的原因是因为我有一个需要停止孙子进程的场景。
这是因为Windows平台上终止孙子进程的方式是使用Win32 API来确定进程关系,或者杀死它们(如Kill Tree的实现)或者调用taskkill程序。
即使我的程序比taskkill3-5倍。(见bench


这是我第一个Rust crate和CLI工具。这意味着我开始它是因为它是一个适合用Rust开发的合适项目。
没有多平台库或CLI可以递归地终止进程。

在过去,各种类型的CLI工具必须被调用来在Node.js项目中实现功能。
这是一个应该能够运行用于通用目的的shell脚本的项目,例如CI/CD工具。
在Windows平台上的Node.js环境中,我遇到了一个问题,即通过cmd创建子进程并运行应用程序或批处理脚本不会终止孙子进程。
原因在于Windows平台没有信号
我必须调用taskkill来取消或强制杀死用户在Windows平台上运行的嵌套shell脚本。
当终止分布式应用程序时,我希望所有子进程都被终止。
因此,我使用了Npm.js中的一个名为tree-kill的库来解决它。从功能上来说,我对此很满意。

然而,如果下次因为类似的原因需要这个功能,我需要安装Node.js并获取tree-kill库来运行它。
这会创建一个深层依赖,因为它内部调用了CLI工具,如taskkill。
总之,需要Node.js运行时(包括npm包管理器)、tree-kill包和taskkill CLI工具。
如果我用npx或封装的Node.js脚本运行它,需要相当长的时间。
因为它首先加载和评估脚本,然后从脚本中调用taskkill来终止子进程。

通常需要在终端环境中终止进程,每个平台有不同的命令。
例如,kill -9 777taskkill /T /F /PID 777

因此,如果这是一个Rust项目,我可以在代码中直接递归地终止进程。
我还统一了接口,因为各个平台上的进程终止CLI工具各不相同。

为什么使用Rust

Rust是一种“电池式”系统编程语言,没有垃圾回收器,具有包管理器、基本包注册中心和各种实用功能。
它还支持语言级别的异步编程模型,消除了对绿色线程的需求。

我从C开始编程之旅,主要使用C++,还探索了Go和JavaScript(包括TypeScript和Node.js)。
与C和C++缺乏主流的包管理器和注册中心,以及Node.js依赖于单线程事件循环不同,Rust提供了一个全面的生态系统。
Go的“电池式”方法,使用goroutines和channels进行并发,很有吸引力,但Rust的功能使我改变了主意。
(如果我不知道Rust,我可能已经在Go上妥协了。😉)

这就是为什么我使用Rust,而且使用它的时间不长,所以我想我会尝试更多。

为什么使用Tokio

我喜欢基于任务的并行处理

Tokio是一个用Rust编写的异步运行时框架,也是最受欢迎的之一。
在当前环境中,标准异步运行时尚未可用,我认为使用Tokio是一个相当合理的选项。
Tokio的强大功能和众多特性帮助我专注于业务逻辑。

当我第一次学习编程时,我学习了基于角色的并行处理,并且我正在使用它。
例如,UI线程(主线程)、http请求线程、加载线程等。
这种方法不能保证线程之间的均匀吞吐量,可能会过载某些线程。
然而,基于任务的并行处理可能会平衡线程之间的吞吐量,从而不会浪费计算资源。

Rust编写的Tokio运行时允许我进行零成本基于任务的并行处理,而无需关注实现的细节。

如何使用

作为CLI工具使用

安装方法

🚨 请,查看脚本内容。始终注意安全!🚨
如果有一种安全安装可执行文件的方法,如果您能告诉我,我将不胜感激!

复制到剪贴板,粘贴到shell中并按Enter键。

Windows

iwr https://raw.githubusercontent.com/oneofthezombies/kill-tree/main/installs/windows.ps1 -UseBasicParsing | iex

Linux

sudo curl -sSL https://raw.githubusercontent.com/oneofthezombies/kill-tree/main/installs/linux.sh | sudo sh

Macos

sudo curl -sSL https://raw.githubusercontent.com/oneofthezombies/kill-tree/main/installs/macos.sh | sudo sh

以下是向进程ID为 777 的进程及其所有子进程发送 SIGTERM 信号的示例。

ℹ️ 在 Windows 平台上,包括 SIGTERM 和 SIGKILL 在内的所有信号都被忽略。

kill-tree 777

如果您想发送另一个信号,可以将该信号作为第二个参数输入。
以下是向进程ID为 777 的进程及其所有子进程发送 SIGKILL 信号的示例。

kill-tree 777 SIGKILL

作为Rust库使用

同步方法

kill_tree 添加到您的依赖项中。

# Cargo.toml
[dependencies]
kill_tree = "0.2"

同步API使用 kill_tree::blocking 启动。

使用默认信号 SIGTERM 递归地杀死进程及其子进程。
当函数被调用时返回进程信息列表。
进程信息是 KilledMaybeAlreadyTerminated
如果进程信息是 Killed 类型,它包含 process_idparent_process_idname
MaybeAlreadyTerminated 类型,它包含 process_idsource

由于它们可以在查询和杀死进程的过程中被杀死,所以有两种类型。
因此,即使查询或杀死进程看起来失败,也要考虑操作成功。
这是因为该库的目的是使进程达到 not exist 状态。

use kill_tree::{blocking::kill_tree, Output, Result};

fn main() -> Result<()> {
    let process_id = 777;
    let outputs = kill_tree(process_id)?;
    for (index, output) in outputs.iter().enumerate() {
        match output {
            Output::Killed {
                process_id,
                parent_process_id,
                name,
            } => {
                println!(
                    "[{index}] Killed process. process id: {process_id}, parent process id: {parent_process_id}, name: {name}"
                );
            }
            Output::MaybeAlreadyTerminated { process_id, source } => {
                println!(
                    "[{index}] Maybe already terminated process. process id: {process_id}, source: {source}"
                );
            }
        }
    }
    Ok(())
}

使用信号 SIGKILL 递归地杀死进程及其子进程。

use kill_tree::{blocking::kill_tree_with_config, Config, Result};

fn main() -> Result<()> {
    let process_id = 777;
    let config = Config {
        signal: "SIGKILL".to_string(),
        ..Default::default()
    };
    let outputs = kill_tree_with_config(process_id, &config)?;
    println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
    Ok(())
}

如果当 ctrl + c (command + c) 事件发生时,您想递归地杀死除当前进程之外的所有子进程。

use kill_tree::{blocking::kill_tree_with_config, Config};
use std::sync::mpsc::channel;

fn cleanup_children() {
    let current_process_id = std::process::id();
    let config = Config {
        include_target: false,
        ..Default::default()
    };
    let result = kill_tree_with_config(current_process_id, &config);
    println!("kill_tree_with_config: {result:?}");
}

fn main() {
    let (tx, rx) = channel();

    ctrlc::set_handler(move || {
        cleanup_children();
        tx.send(()).expect("Could not send signal on channel.");
    })
    .expect("Error setting handler.");

    println!("Current process id: {}", std::process::id());
    println!("Waiting for signal...");
    rx.recv().expect("Could not receive from channel.");
    println!("Got it! Exiting...");
}

异步方法

使用带有功能 tokiokill_tree 添加到您的依赖项中。

# Cargo.toml
[dependencies]
kill_tree = { version = "0.2", features = ["tokio"] }

同步API使用 kill_tree::tokio 启动。

使用默认信号 SIGTERM 递归地杀死进程及其子进程。

use kill_tree::{get_available_max_process_id, tokio::kill_tree, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let outputs = kill_tree(get_available_max_process_id()).await?;
    println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
    Ok(())
}

在发送其他信号或接收和处理 ctrl + c 事件时,您只需将上面的 kill_tree::blocking API 更改为 kill_tree::tokio 并执行 await 处理,它将等效。

支持平台和架构

平台 架构 支持
Windows x86_64
Windows aarch64 未测试
Linux x86_64
Linux aarch64 未测试
Macos x86_64
Macos aarch64

此CLI和库依赖于操作系统的系统库。
因为操作系统拥有进程。
但不用担心!这是一个OS默认库,因此无需安装任何附加内容!

平台 依赖项
Windows kernel32.dll
oleaut32.dll
ntdll.dll
advapi32.dll
bcrypt.dll
Linux -
Macos libiconv.dylib
libSystem.dylib

依赖项

~2–39MB
~584K SLoC