Rust+Nix实现两子进程消息收发遇阻塞无输出问题求助
解决Rust子进程管道阻塞问题
你的代码出现阻塞主要有几个核心问题,下面逐一说明并给出修复方案:
问题分析
read_to_string的阻塞特性:这个方法会持续读取直到遇到EOF,但管道的写端始终未关闭,导致进程一直等待,无法继续执行。- 管道句柄未正确清理:多个进程持有同一管道的读写端,导致管道的EOF信号无法正确触发。
- 进程等待不完整:父进程仅调用一次
wait(),无法等待两个子进程全部结束,可能导致僵尸进程。 - 循环次数错误:
1..10仅执行9次循环,不符合题目要求的10次。
修复后的代码
use nix::sys::wait::{wait, WaitStatus}; use nix::unistd::ForkResult::{Child, Parent}; use nix::unistd::fork; use std::io::prelude::*; use std::time::Duration; fn main() { // 创建双向通信管道:C2 -> C1 的发送管道,C1 -> C2 的回复管道 let (c1_msg_reader, mut c2_msg_writer) = os_pipe::pipe().unwrap(); let (mut c2_reply_reader, c1_reply_writer) = os_pipe::pipe().unwrap(); // 创建第一个子进程(C1) match fork().expect("Failed to fork child process 1") { Parent { child: c1_pid } => { // 父进程不需要C1的管道句柄,立即关闭 drop(c1_msg_reader); drop(c1_reply_writer); // 创建第二个子进程(C2) match fork().expect("Failed to fork child process 2") { Parent { child: c2_pid } => { // 父进程关闭C2的管道句柄 drop(c2_msg_writer); drop(c2_reply_reader); // 循环等待两个子进程全部结束 let mut completed = 0; while completed < 2 { match wait().unwrap() { WaitStatus::Exited(pid, _) | WaitStatus::Signaled(pid, _, _) => { if pid == c1_pid || pid == c2_pid { completed += 1; } } _ => continue, } } } Child => { // C2逻辑:发送消息 -> 接收回复 -> 休眠,重复10次 drop(c1_reply_writer); drop(c1_msg_reader); for round in 1..=10 { // 发送固定长度消息到C1 c2_msg_writer.write_all("C2_MSG".as_bytes()).unwrap(); c2_msg_writer.flush().unwrap(); // 读取C1的回复(固定长度:7字节) let mut buf = [0u8; 7]; c2_reply_reader.read_exact(&mut buf).unwrap(); let reply = String::from_utf8_lossy(&buf); println!("C2 received reply: {}", reply); std::thread::sleep(Duration::from_secs(2)); println!("--- Round {} finished ---", round); } // 关闭写端,通知C1通信结束 drop(c2_msg_writer); } } } Child => { // C1逻辑:接收消息 -> 回复消息,重复10次 drop(c2_msg_writer); drop(c2_reply_reader); for _ in 1..=10 { // 读取C2发送的消息(固定长度:7字节) let mut buf = [0u8; 7]; c1_msg_reader.read_exact(&mut buf).unwrap(); let msg = String::from_utf8_lossy(&buf); println!("C1 received message: {}", msg); // 回复相同的消息给C2 c1_reply_writer.write_all(msg.as_bytes()).unwrap(); c1_reply_writer.flush().unwrap(); } // 关闭管道句柄 drop(c1_msg_reader); drop(c1_reply_writer); } } }
关键修改说明
- 替换
read_to_string为read_exact:由于我们明确知道每次传输的消息长度,使用read_exact读取固定字节数,避免等待EOF导致的阻塞。 - 严格清理管道句柄:每个进程仅保留自己需要的管道端,其余全部
drop,确保管道的EOF信号能在合适时机触发。 - 完整等待子进程:父进程循环等待,直到两个子进程都结束,避免僵尸进程残留。
- 修正循环次数:使用
1..=10确保执行10次通信循环。 - 添加
flush():显式刷新管道写缓冲区,确保数据立即发送到管道,避免数据滞留。 - 明确变量命名:重命名管道变量,清晰区分消息管道和回复管道,提升代码可读性。
内容的提问来源于stack exchange,提问作者danixl30 ct
相关产品推荐
相关产品推荐

