29 个版本 (10 个稳定版)

1.2.1 2024 年 8 月 1 日
1.2.0 2024 年 6 月 5 日
1.1.5 2023 年 12 月 29 日
1.1.4 2023 年 4 月 22 日
0.1.0 2016 年 7 月 12 日

#24 in 操作系统

Download history 195390/week @ 2024-05-03 210087/week @ 2024-05-10 215606/week @ 2024-05-17 208132/week @ 2024-05-24 226585/week @ 2024-05-31 228139/week @ 2024-06-07 216581/week @ 2024-06-14 218468/week @ 2024-06-21 215486/week @ 2024-06-28 212919/week @ 2024-07-05 222016/week @ 2024-07-12 226310/week @ 2024-07-19 241263/week @ 2024-07-26 243972/week @ 2024-08-02 268959/week @ 2024-08-09 288569/week @ 2024-08-16

1,091,336 每月下载量
728 个Crate中使用 (92 个直接使用)

MIT 许可证

34KB
459

os_pipe.rs Actions 状态 crates.io docs.rs

一个跨平台库,用于打开操作系统管道,如Linux中的pipe或Windows中的CreatePipe。Rust标准库为涉及子进程的简单用例提供了Stdio::piped,但它不支持直接创建管道。这个crate填补了这个空白。

当您使用管道时,您可能会在某个时候遇到死锁问题。如果您不知道为什么会发生这些问题,它们可能会令人困惑。以下是你需要了解的两个关键点

  1. 管道读取将在至少有一个写入器仍然打开的情况下等待输入。如果您忘记关闭一个写入器,读取将永远阻塞。这包括您提供给子进程的写入器。
  2. 管道有一个固定大小的内部缓冲区。例如,在Linux中,默认情况下管道缓冲区大小为64 KiB。当缓冲区满时,写入将阻塞等待空间。如果缓冲区已满且没有读取器,写入将永远阻塞。

由忘记写入者引起的死锁通常立即出现,一旦你知道该寻找什么,它们就相对容易修复。(请参见下面示例代码中的“避免死锁!”。)然而,由满管道缓冲区引起的死锁更复杂。这些可能仅在较大的输入时出现,并且可能是时间相关或平台相关的。如果你发现有时写入管道会导致死锁,请考虑谁应该从该管道中读取,以及该线程或进程可能是否被其他事物阻塞。有关更多信息,请参阅来自duct crate 的duct 文档中的“注意事项”(考虑duct 是否适合你的用例)。

示例

这里我们将单个字节写入管道并读取出来

use std::io::prelude::*;

let (mut reader, mut writer) = os_pipe::pipe()?;
// XXX: If this write blocks, we'll never get to the read.
writer.write_all(b"x")?;
let mut output = [0];
reader.read_exact(&mut output)?;
assert_eq!(b"x", &output);

这是一个最小的工作示例,但如上所述,在同一个线程上进行读写操作容易引起死锁。如果我们写入100 KB而不是一个字节,这个例子将阻塞在write_all上,它将永远无法到达read_exact,这将是一个死锁。从不同的线程或不同的进程中执行读写操作可以解决死锁。

对于更复杂的示例,我们将子进程的stdout和stderr连接到单个管道中。为此,我们打开一个管道,克隆其写入器,并将这对写入器设置为子进程的stdout和stderr。(这是可能的,因为PipeWriter 实现了 Into<Stdio>。)然后我们可以从管道读取器读取交错输出。这个例子没有死锁,但请注意有关关闭写入器的注释。

// We're going to spawn a child process that prints "foo" to stdout
// and "bar" to stderr, and we'll combine these into a single pipe.
let mut command = std::process::Command::new("python");
command.args(&["-c", r#"
import sys
sys.stdout.write("foo")
sys.stdout.flush()
sys.stderr.write("bar")
sys.stderr.flush()
"#]);

// Here's the interesting part. Open a pipe, clone its writer, and
// set that pair of writers as the child's stdout and stderr.
let (mut reader, writer) = os_pipe::pipe()?;
let writer_clone = writer.try_clone()?;
command.stdout(writer);
command.stderr(writer_clone);

// Now start the child process running.
let mut handle = command.spawn()?;

// Avoid a deadlock! This parent process is still holding open pipe
// writers inside the Command object, and we have to close those
// before we read. Here we do this by dropping the Command object.
drop(command);

// Finally we can read all the output and clean up the child.
let mut output = String::new();
reader.read_to_string(&mut output)?;
handle.wait()?;
assert_eq!(output, "foobar");

请注意,duct crate 可以用一行代码重现上面的示例,没有死锁的风险,也没有泄露僵尸进程的风险。

依赖项

~0–8.5MB
~64K SLoC