You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust+Nix实现两子进程消息收发遇阻塞无输出问题求助

解决Rust子进程管道阻塞问题

你的代码出现阻塞主要有几个核心问题,下面逐一说明并给出修复方案:

问题分析

  1. read_to_string的阻塞特性:这个方法会持续读取直到遇到EOF,但管道的写端始终未关闭,导致进程一直等待,无法继续执行。
  2. 管道句柄未正确清理:多个进程持有同一管道的读写端,导致管道的EOF信号无法正确触发。
  3. 进程等待不完整:父进程仅调用一次wait(),无法等待两个子进程全部结束,可能导致僵尸进程。
  4. 循环次数错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 13:16:00