Rust 下通过配置 Job Object 实现在主进程退出时自动清理创建的子进程
之前写 Tauri 应用(没错就是BiliTools)时,需要在后台挂个 aria2c 实现多线程下载
然后发现很尴尬这玩意不会跟着应用退出而退出(理所当然)
本来想的是写一个结构体,存 Child,然后监听窗口关闭事件 tauri::WindowEvent::Destroyed
,调用函数把进程杀了
这种方法能用是能用吧,但是如果 Panic,非正常关闭,应用更新什么的就会比较麻烦
不如找个一劳永逸的方法,就不用到处设 hook 监听退出了
方案一
这种方法 macOS / 其他Linux发行版 / Windows 都适用
就是运行一个监控脚本,传递主进程 PID 和子进程 PID
脚本定期检查主进程是否退出,一旦退出就杀掉指定子进程
这种方法的坏处就是需要在每个运行子进程的地方都设置一个监控
Windows
1 2 3 4
| param($a,$b)
while ((Get-Process -Id $a -ErrorAction SilentlyContinue) -ne $null) \ {{ Start-Sleep -Milliseconds 500 }}; Stop-Process -Id $b -Force
|
实践:
main.rs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use std::process::{Command, self};
fn main() -> Result<(), Box<dyn std::error::Error>> { let child = Command::new("aria2c").spawn()?; let child_id = child.id(); let main_id = process::id();
Command::new("powershell.exe") .arg("-Command") .arg(format!( "while ((Get-Process -Id {} -ErrorAction SilentlyContinue) -ne $null) \ {{ Start-Sleep -Milliseconds 500 }}; Stop-Process -Id {} -Force", main_id, child_id)) .spawn()?;
Ok(()) }
|
macOS / 其他Linux发行版
1
| while kill -0 $1 2>/dev/null; do sleep 0.5; done; kill $2
|
实践:
main.rs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| use std::process::{Command, self};
fn main() -> Result<(), Box<dyn std::error::Error>> { let child = Command::new("aria2c").spawn()?; let child_id = child.id(); let main_id = process::id();
Command::new("/bin/bash") .arg("-c") .arg(format!( "while kill -0 {} 2>/dev/null; do sleep 0.5; done; kill {}", main_id, child_id)) .spawn()?;
Ok(()) }
|
方案二(仅适用 Windows)
WinAPI 下有个叫 Job Object / 作业对象
的东西,可以关联进程,从而实现退出时终止与作业关联的所有进程
Rust 中有两种方式实现它:
下面是 win32job
的示例
main.rs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| use win32job::Job; use std::process::Command;
fn main() -> Result<(), Box<dyn std::error::Error>> { let job = Job::create()?; let mut info = job.query_extended_limit_info()?; info.limit_kill_on_job_close(); job.set_extended_limit_info(&mut info)?; job.assign_current_process()?;
Command::new("aria2c.exe").spawn()?;
Ok(()) }
|