【Rust多进程】征服CPU的艺术:Rust多进程实战指南

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Rust高性能并发编程
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • Rust多进程
    • 一、Rust进程
    • 二、std::process 模块简介
    • 三、创建子进程(Command 和 spawn)
      • 3.1 最简单的例子
      • 3.2 Command 的常用方法
    • 四、捕获输出(Output 和管道)
      • 4.1 与 spawn() 的对比
      • 4.2 异步读取子进程输出
    • 五、标准流重定向(Stdio)
      • 5.1 基本用法
      • 5.2 写入子进程 stdin
    • 六、进程状态与控制
      • 6.1 等待进程退出
      • 6.2 检查是否退出
      • 6.3 强制终止子进程
    • 七、进程返回值与错误处理
      • 7.1 ExitStatus 检查
      • 7.2 处理执行失败
    • 八、跨平台注意事项
    • 九、高级进程控制
      • 9.1 管道组合多个子进程
      • 9.2 与异步库结合(Tokio)
      • 9.3 超时控制
    • 十、实战:实现一个 shell-like 管道程序
    • 十一、常见错误排查
    • 十二、总结

Rust多进程

一、Rust进程

在系统编程中,进程(Process)是一个基本而重要的概念。在 Rust 中,我们可以使用标准库 std::process 来启动、管理和与子进程进行通信。
本文将深入讲解如何在 Rust 中使用 std::process 模块创建和控制子进程,并提供实际案例帮助你掌握其用法。

二、std::process 模块简介

Rust 提供了以下几个核心类型用于进程管理:
std::process::Command:用于配置和启动新进程
std::process::Child:表示运行中的子进程
std::process::ExitStatus:子进程的退出状态
std::process::Output:表示完整子进程输出
std::process::Stdio:用于标准输入输出重定向
这些类型构成了 Rust 进程操作的主干。

三、创建子进程(Command 和 spawn)

3.1 最简单的例子

use std::process::Command;fn main() {//创建子进程//Command::new()的参数是要执行的命令//arg()的参数是命令的参数//status()返回一个Result<ExitStatus, io::Error>let status = Command::new("ls").arg("-la").status().expect("failed to execute process");//打印子进程的退出状态println!("Process exited with: {}", status);
}

包含退出状态码和命令执行的标准输出结果
该代码会启动 ls -la 子进程并等待其退出。
在这里插入图片描述

3.2 Command 的常用方法

arg(&str) 添加单个参数
args(&[&str]) 添加多个参数
env() 设置环境变量
envs(iter) 批量设置多个环境变量(接受 key-val 对)
env_remove(key) 移除一个环境变量
env_clear() 清除继承的环境变量
current_dir() 设置工作目录
stdin(Stdio) 配置子进程的标准输入
stdout(Stdio) 配置子进程的标准输出
stderr(Stdio) 配置子进程的标准错误

常用的 Stdio 构造函数:
Stdio::inherit():继承父进程(默认行为)
Stdio::piped():用管道连接,可以读写
Stdio::null():忽略该流
Command::new(“cat”).stdin(Stdio::piped());

执行进程的方法
spawn() std::process::Child 启动子进程(非阻塞)
status() std::process::ExitStatus 等待进程完成并返回状态
output() std::process::Output 等待进程并捕获 stdout 和 stderr
arg0() 设置 argv[0](对程序看到的名称)

其他辅助方法
arg0(name) 设置 argv[0],用于伪装程序名
uid(uid: u32) 设置子进程的用户 ID(Unix)
gid(gid: u32) 设置组 ID(Unix)
before_exec() 设置进程启动前的钩子(危险,需 unsafe)
这些方法仅在特定平台(如 Unix)生效。

示例:设置环境变量和工作目录

Command::new("echo").arg("$MY_VAR").env("MY_VAR", "HelloRust").current_dir("/tmp").status().expect("failed");

四、捕获输出(Output 和管道)

使用 output() 方法可以捕获 stdout 和 stderr:

use std::process::Command;fn main() {//获取子进程的输出let output = Command::new("echo").arg("hello world").output().expect("failed to execute");//打印子进程状态println!("status: {}", output.status);//打印子进程的标准输出println!("stdout: {}", String::from_utf8_lossy(&output.stdout));//打印子进程的标准错误println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
}

在这里插入图片描述

4.1 与 spawn() 的对比

output():阻塞直到进程退出并返回所有输出
spawn():返回 Child,可以异步控制进程

4.2 异步读取子进程输出

use std::process::{ Command, Stdio };
use std::io::{ BufReader, BufRead };fn main() {// 创建子进程let mut child = Command::new("ping").arg("localhost").stdout(Stdio::piped()) // 重定向子进程的标准输出到管道.spawn().expect("failed to start ping");// 读取子进程的输出let stdout = child.stdout.take().unwrap();let reader = BufReader::new(stdout);// 打印前5行输出for line in reader.lines().take(5) {println!("ping: {}", line.unwrap());}child.kill().unwrap(); // 停止 ping
}

在这里插入图片描述

五、标准流重定向(Stdio)

5.1 基本用法

Stdio::inherit() 继承父进程流
Stdio::piped() 使用管道进行通信
Stdio::null() 忽略输入输出

use std::process::{Command, Stdio};fn main() {Command::new("myapp").stdout(Stdio::null())  // 忽略输出.stderr(Stdio::inherit())  // 输出错误到终端.spawn().expect("fail");
}

5.2 写入子进程 stdin

use std::process::{ Command, Stdio };
use std::io::Write;fn main() {//创建子进程let mut child = Command::new("cat").stdin(Stdio::piped()).stdout(Stdio::inherit()) //将子进程的输出重定向到父进程的输出.spawn().expect("fail");//获取子进程的输入流let mut stdin = child.stdin.take().unwrap();//主进程向子进程写入数据stdin.write_all(b"Hello from parent!\n").unwrap();
}

在这里插入图片描述

六、进程状态与控制

6.1 等待进程退出

let mut child = Command::new("sleep").arg("2").spawn().unwrap();let status = child.wait().unwrap();
println!("Exited with: {}", status);

6.2 检查是否退出

if let Some(status) = child.try_wait().unwrap() {println!("Exited: {}", status);
} else {println!("Still running");
}

6.3 强制终止子进程

child.kill().unwrap();

七、进程返回值与错误处理

7.1 ExitStatus 检查

if status.success() {println!("Process succeeded");
} else {println!("Exit code: {:?}", status.code());
}

7.2 处理执行失败

match Command::new("not_exist_app").status() {Ok(status) => println!("Exit code: {}", status),Err(e) => eprintln!("Failed to run: {}", e),
}

八、跨平台注意事项

Windows 与 Unix 指令不同,如 dir vs ls
有些应用需要 .cmd 扩展名
路径分隔符差异(\ vs /)
Windows 可能不支持 stdout.piped() 某些行为

跨平台建议使用:

#[cfg(target_os = "windows")]
let cmd = "cmd";
#[cfg(target_os = "linux")]
let cmd = "sh";

九、高级进程控制

9.1 管道组合多个子进程

echo “foo bar baz”|grep foo这个命令会创建两个子进程,一个作为生产者,一个作为消费者。生产者进程会将数据写入管道,而消费者进程会从管道中读取数据。

use std::process::{ Command, Stdio };fn main() {let mut grep = Command::new("grep").arg("foo").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().unwrap();let stdin = grep.stdin.take().unwrap(); // 提前 move 掉// echo 会把 stdout 重定向到 grep 的 stdinlet mut echo = Command::new("echo").arg("foo bar baz").stdout(stdin).spawn().unwrap();echo.wait().unwrap(); // 等 echo 结束let output = grep.wait_with_output().unwrap(); // 等 grep 结束println!("{}", String::from_utf8_lossy(&output.stdout));
}

在这里插入图片描述

代码解读
这段代码是一个典型的 Rust 进程管道通信 示例,相当于在 shell 中执行:
echo “foo bar baz” | grep foo
也就是把 echo 命令的输出,作为 grep 命令的输入,并读取 grep 的输出。

✅ 分段讲解

use std::process::{Command, Stdio};
use std::io::Write;

引入 Command 和 Stdio 来启动子进程和配置标准流。
引入 Write 是为了可以写入 stdin(虽然本例没直接使用写操作,但是预备的)。

启动 grep 子进程,并配置好标准流

let mut grep = Command::new("grep").arg("foo").stdin(Stdio::piped())   // grep 的 stdin 用管道接收输入.stdout(Stdio::piped())  // grep 的 stdout 用管道输出.spawn().unwrap();

这就创建了一个 grep foo 的子进程,准备接收数据、过滤包含 “foo” 的内容。

拿到 grep 的标准输入端

let grep_stdin = grep.stdin.take().unwrap();

grep.stdin 是一个 Option<ChildStdin>

take() 会把它移出,让我们可以传给另一个进程作为输出目标。

启动 echo 子进程,把 stdout 接到 grep 的 stdin

let mut echo = Command::new("echo").arg("foo bar baz").stdout(grep_stdin) // 把 echo 的输出直接作为 grep 的输入.spawn().unwrap();

关键点:stdout(grep_stdin) 表示:
echo 的标准输出会写入 grep 的标准输入。
形成管道效果(echo --> grep)。

等待 echo 完成
echo.wait().unwrap();

等待 echo 输出完成,确保数据已经写入 grep。

获取 grep 的输出结果
let output = grep.wait_with_output().unwrap();
println!(“输出: {}”, String::from_utf8_lossy(&output.stdout));

等待 grep 处理完数据并退出。
打印 grep 的输出结果(过滤后的数据)。

✅ 运行结果
这个例子的最终输出将是:
foo bar baz

因为:
echo 输出的是 “foo bar baz”
grep foo 过滤后,保留这行

9.2 与异步库结合(Tokio)

添加依赖Cargo.toml

[dependencies]
tokio = { version = "1.47.0", features = ["full"] }
use tokio::process::Command;#[tokio::main]
async fn main() {let output = Command::new("echo").arg("hello async").output().await.expect("fail");println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
}

在这里插入图片描述

9.3 超时控制

使用 wait_timeout crate:

use wait_timeout::ChildExt;
use std::time::Duration;
use std::process::Command;fn main() {//创建子进程let mut child = Command::new("sleep").arg("10").spawn().unwrap();//等待子进程,如果在3秒内没有结束,则杀死子进程let status = child.wait_timeout(Duration::from_secs(3)).unwrap();//判断子进程是否结束if let Some(status) = status {//如果子进程正常结束,则打印退出码println!("Exit: {:?}", status.code());} else {//如果子进程超时,则打印超时信息并杀死子进程println!("Timeout, killing...");child.kill().unwrap();}
}

在这里插入图片描述

十、实战:实现一个 shell-like 管道程序

use std::process::{ Command, Stdio };fn main() {let ps = Command::new("ps").arg("aux").stdout(Stdio::piped()).spawn().unwrap();let grep = Command::new("grep").arg("docker").stdin(ps.stdout.unwrap()).stdout(Stdio::piped()).spawn().unwrap();let output = grep.wait_with_output().unwrap();println!("{}", String::from_utf8_lossy(&output.stdout));
}

在这里插入图片描述

十一、常见错误排查

在这里插入图片描述

十二、总结

Rust 提供的 std::process 是一个强大、安全且跨平台的进程管理工具。
通过 Command 配置、spawn 启动、标准流重定向、错误处理、异步集成,我们可以构建高效的进程管控机制,非常适合系统编程和构建 CLI 工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/922001.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/922001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV 高阶实战:图像直方图与掩码图像深度解析

目录 一、图像直方图&#xff1a;读懂图像的 “像素分布报告” 1. 什么是图像直方图&#xff1f; 2. 图像直方图的核心作用 &#xff08;1&#xff09;分析亮度分布 &#xff08;2&#xff09;判断对比度高低 &#xff08;3&#xff09;辅助图像增强与阈值分割 &#xf…

基于stm32的家庭安全监测系统设计

若该文为原创文章&#xff0c;转载请注明原文出处。一、引言&#xff08;一&#xff09;研究背景及意义背景&#xff1a;随着智能家居概念的普及&#xff0c;人们对家庭安全、舒适度和节能提出了更高要求。传统安防系统功能单一、各系统独立&#xff0c;缺乏联动和远程管理能力…

Oracle体系结构-控制文件(Control Files)

一、 原理 (Principle) 核心定位&#xff1a; 控制文件是一个小型的二进制文件&#xff0c;由 Oracle 实例在启动和操作过程中持续读写。它是数据库物理结构的权威记录。数据库无法启动或正常操作时&#xff0c;如果无法访问控制文件&#xff0c;实例将无法识别数据文件和重做日…

路由 下一跳 网关 两个不同网段的ip如何通过路由器互通

路由 (Routing)核心思想&#xff1a;路径选择是什么&#xff1f; 路由是指数据包从源主机传输到目标主机的整个过程。这个过程就像寄快递&#xff1a;你需要决定包裹经过哪些中转站才能最终到达收件人手里。做什么&#xff1f; 网络中的设备&#xff08;主要是路由器&#xff0…

HiDDeN论文解读与代码实现

论文&#xff1a;HiDDeN: Hiding Data With Deep Networks 作者&#xff1a;Jiren Zhu, Russell Kaplan, Justin Johnson, Li Fei-Fei一、研究背景 在图像信息隐藏领域&#xff0c;通常有两类典型的应用场景&#xff1a;隐写 (Steganography) 目标&#xff1a;实现秘密通信。要…

实验室服务器配置|实验室多人共享GPU|通过Docker实现Linux系统多用户隔离与安全防控

利用实验室服务器跑实验的时候&#xff0c;通常就是两种方案&#xff0c;一个是向日葵远程桌面进行操作&#xff0c;一个是通过ssh进行连接&#xff0c;用ssh的话&#xff0c;一般服务器都在内网&#xff08;例如校园网&#xff09;&#xff0c;是无法在公网&#xff08;不在校…

2019考研数学(二)真题

一、选择题 (1) (2) (3) (4) 遗漏点&#xff1a;由通解知特解&#xff0c;特解代入微分方程 (5) ★记住这个题&#xff0c;用的泰勒展开(6) (7) 遗忘点&#xff1a; ★伴随矩阵的秩与原矩阵秩的关系&#xff1a; (8) 错误点&#xff1a;粗心 二、填空题 (9) 易混淆点&#xff…

10 分钟上手 ECharts:从“能跑”到“生产级”的完整踩坑之旅

10 分钟上手 ECharts&#xff1a;从“能跑”到“生产级”的完整踩坑笔记 如果你也曾 复制了官方 Demo 却不知道怎么拆、窗口一拉伸图表就变形、切换标签页后内存暴涨——这篇博客就是为你写的。 我会用 6 个递进版本 的源码&#xff0c;带你把一张 最简柱状图 逐步进化成 可销毁…

二级缓存在实际项目中的应用

二级缓存在项目中的应用 目录 1. 二级缓存简介2. 应用场景3. 重难点分析4. 结合SpringBoot使用5. 最佳实践与案例6. 总结 1. 二级缓存简介 1.1 什么是二级缓存 二级缓存&#xff08;Second-Level Cache&#xff09; 是Hibernate框架中的一个重要特性&#xff0c;它提供了应…

深入浅出CRC校验:从数学原理到单周期硬件实现 (2)CRC数学多项式基础

数学的优雅&#xff1a;剖开CRC的多项式除法核心看似复杂的CRC校验&#xff0c;其核心建立在优雅的数学基础之上。本文将为您揭开CRC算法的数学面纱&#xff0c;让您真正理解多项式除法的精妙之处。模2运算&#xff1a;CRC世界的特殊算术 CRC计算建立在一种特殊的代数系统上——…

软考初级有没有必要考?

对正在学习相关专业的学生或者是行业新人&#xff0c;这篇文章从软考初级的含义、适合哪些人考、考试难度等方面解答&#xff0c;帮助你判断要不要报考。一、软考初级是什么&#xff1f; 软考初级是软考体系里面的基础级别&#xff0c;主要面向在校大学生或是IT行业新人&#x…

11 Prompt 工程进阶:Few-shot 与 Chain-of-Thought

11 Prompt 工程进阶&#xff1a;Few-shot 与 Chain-of-Thought 前10节总结 & 后10节展望 在前 10 节&#xff0c;我们已经完成了 AI 产品经理的入门阶段&#xff1a; 1–3&#xff1a;理解了大模型的基本概念、Token、Prompt 基础&#xff1b;4–5&#xff1a;体验了本地部…

ARM1.(ARM体系结构)

1.基本概念嵌入式:以应用为心&#xff0c;以计算机技术为础&#xff0c;软便件可被的专用计算机系统。计算机系统的软件基本组成: 系统软件、应用软件。计算机系统的硬件基本组成&#xff1a;运算器、控制器、存诸器、输入设备、输出设备日常生活中遇到的专业术语&#xff1a…

Django全栈班v1.01 Python简介与特点 20250910

从零开始的Python编程之旅 “人生苦短&#xff0c;我用Python。”这不仅仅是Python程序员的口头禅&#xff0c;更是对Python强大能力的最好诠释&#xff01;&#xff01;&#xff01; 为什么全世界有超过1500万开发者选择Python&#xff1f; 为什么Python连续多年蝉联最受欢…

【WebApi】什么情况开启如何开启缓存

在 ASP.NET Core WebAPI 中开启缓存是优化性能、减少服务器负载和提升用户体验的非常重要的手段。但并非所有情况都适合开启缓存。 下面我将从 “什么情况下开启” 和 “如何开启” 两个方面为你详细解释。 一、什么情况下应该开启缓存? 总的来说,缓存适用于 “变化不频繁但…

Go语言类型断言全解析

类型断言的基本概念类型断言(Type Assertion)是Go语言中用于检查接口值底层具体类型的机制。它本质上是一种运行时类型检查的操作&#xff0c;允许程序在运行时判断接口变量是否持有特定的类型值&#xff0c;并提取该类型的值。这是Go语言类型系统中的一个重要特性&#xff0c;…

大模型在题目生成中的安全研究:攻击方法与防御机制

大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制 文章目录大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制一、引言二、大模型在题目生成中的安全漏洞与攻击方法2.1 大模型在题目生成中的安全漏洞分析2.1.1 训练数据相关漏洞2.1.2 模型架构与特性相关…

跟做springboot尚品甄选项目(二)

登录功能的书写 后端接口的书写 &#xff08;1&#xff09;创建配置文件 粘贴这两个文件&#xff08;E:\project\AllProJect\Shangpin Selection\项目材料素材\资料\资料\03-配置文件&#xff09; 在spzx-manager服务的src/resources目录下创建application.yml、application-…

前后端接口调试提效:Postman + Mock Server 的工作流

前后端接口调试提效&#xff1a;Postman Mock Server 的工作流 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都是…

大带宽香港云服务器在数据传输速度上有何优势?

为方便站长快速部署网站、优化用户访问体验&#xff0c;当下众多实力强劲的香港数据中心&#xff0c;均推出了大带宽云服务器产品。不过&#xff0c;市面上不少数据中心虽宣称提供 “专属大带宽”&#xff0c;但其线路配置中&#xff0c;国际线路占比高、绕行链路多&#xff0c…