当前位置:首页>Linux>linux rust父子进程匿名管道通信实现方式

linux rust父子进程匿名管道通信实现方式

  • 2026-09-02 20:09:18
linux rust父子进程匿名管道通信实现方式

golang匿名管道通信写法:

	producerReader, producerWriter, err := os.Pipe()
if err != nil {
return
}
cmd := exec.CommandContext(ctx, transcodePath, args...)
cmd.ExtraFiles = []*os.File{producerReader}

Go 代码的核心逻辑是:

  • os.Pipe() 创建 匿名管道
  • 父进程持有 producerWriter
  • 子进程通过 ExtraFiles 继承 producerReader
  • 子进程里该 FD 会从 3 开始

Rust 可以用 std::os::unix::io + CommandExt::pre_exec 或 Stdio::from 实现类似行为。

下面介绍 生产级 Rust 写法(等价 Go ExtraFiles)。


一、最接近 Go ExtraFiles 的 Rust 写法

Go:

producerReader, producerWriter, _ := os.Pipe()
cmd := exec.CommandContext(ctx, transcodePath, args...)
cmd.ExtraFiles = []*os.File{producerReader}

Rust:

use std::process::{Command};
use std::os::unix::io::{FromRawFd, IntoRawFd};
use nix::unistd::pipe;
use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {

// 创建匿名管道
let (reader_fd, writer_fd) = pipe().unwrap();

let reader = unsafe { File::from_raw_fd(reader_fd) };
let mut writer = unsafe { File::from_raw_fd(writer_fd) };

let mut cmd = Command::new("transcodePath");

    cmd.arg("arg1");

// 关键:传递 FD 给子进程
unsafe {
        cmd.pre_exec(move || {
use nix::unistd::dup2;

// Go ExtraFiles[0] -> fd=3
            dup2(reader_fd, 3).unwrap();

Ok(())
        });
    }

let mut child = cmd.spawn()?;

// 父进程写数据
    writer.write_all(b"hello child\n")?;

let _ = child.wait()?;

Ok(())
}

二、子进程如何读取 FD=3

子进程(Rust / C / Go)读取:

Rust:

use std::fs::File;
use std::os::unix::io::FromRawFd;
use std::io::Read;

fn main() {

let mut pipe = unsafe { File::from_raw_fd(3) };

let mut buf = String::new();

    pipe.read_to_string(&mut buf).unwrap();

println!("recv: {}", buf);
}

三、FD 对齐关系

Go ExtraFiles 规则:

ExtraFiles index

子进程 fd

0

3

1

4

2

5

Rust 对齐方式:

dup2(reader_fd, 3)
dup2(reader_fd2, 4)
dup2(reader_fd3, 5)

四、Rust 更现代写法(推荐)

Rust 1.70+ 可以直接:

use std::process::{Command, Stdio};
use std::os::unix::io::FromRawFd;
use nix::unistd::pipe;
use std::fs::File;

fn main() {

let (r, w) = pipe().unwrap();

let reader = unsafe { File::from_raw_fd(r) };

let mut cmd = Command::new("transcodePath");

    cmd.stdin(Stdio::from(reader));

let _child = cmd.spawn().unwrap();
}

但这会变成 stdin 管道,而不是 fd=3 ExtraFiles。


五、真正生产级实现(接近 Go runtime)

如果你需要 完全模拟 Go ExtraFiles 行为:

fn spawn_with_extra_files(
    program: &str,
    args: &[&str],
    extra_files: Vec<i32>,
) -> std::io::Result<std::process::Child> {

use std::process::Command;
use nix::unistd::dup2;

let mut cmd = Command::new(program);

    cmd.args(args);

unsafe {
        cmd.pre_exec(move || {

for (i, fd) in extra_files.iter().enumerate() {
let target = 3 + i asi32;
                dup2(*fd, target).unwrap();
            }

Ok(())
        });
    }

    cmd.spawn()
}

调用:

let (r, w) = pipe().unwrap();

let child = spawn_with_extra_files(
"transcodePath",
    &[],
vec![r],
)?;

六、Go vs Rust 对比

功能

Go

Rust

pipe

os.Pipe()

nix::unistd::pipe()

ExtraFiles

cmd.ExtraFiles

pre_exec + dup2

FD映射

自动

手动

子进程读取

fd=3

fd=3


七、FFmpeg / Media 场景注意

如果你是:

  • FFmpeg pipe
  • Rust media pipeline
  • MCU / transcoder

推荐:

pipe2(O_CLOEXEC)

Rust:

use nix::unistd::pipe2;
use nix::fcntl::OFlag;

let (r, w) = pipe2(OFlag::O_CLOEXEC).unwrap();

否则可能 FD 泄露。


八、真正工业级做法(Tokio版本)

如果你是 Tokio async runtime,建议:

tokio::process::Command

配合

tokio::io::unix::AsyncFd

实现 异步管道通信。

最新文章

随机文章