golang匿名管道通信写法:
producerReader, producerWriter, err := os.Pipe()
if err != nil {
return
}
cmd := exec.CommandContext(ctx, transcodePath, args...)
cmd.ExtraFiles = []*os.File{producerReader}
Go 代码的核心逻辑是:
- 子进程通过 ExtraFiles 继承 producerReader
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
实现 异步管道通信。