为什么从退出的PTY过程中读取返回“输入/输出错误”在生锈?

发布于 2025-01-27 01:44:38 字数 2119 浏览 2 评论 0原文

我正在尝试从Rust中的PTY支持的过程中读取,但是一旦从过程中读取所有字节,然后从过程中读取INPUT/OUTPUT/OUTPER ERROR而不是预期的EOF返回。是否有明显的原因?阅读?

这是一个最小的工作示例:

use std::io;
use std::io::Read;
use std::io::Write;
use std::fs::File;
use std::os::unix::io::FromRawFd;
use std::process::Command;
use std::process::Stdio;

extern crate nix;

use crate::nix::pty;
use crate::nix::pty::OpenptyResult;

fn main() {
    let OpenptyResult{master: controller_fd, slave: follower_fd} =
        pty::openpty(None, None)
            .expect("couldn't open a new PTY");

    let new_follower_stdio = || unsafe { Stdio::from_raw_fd(follower_fd) };

    let mut child =
        Command::new("ls")
            .stdin(new_follower_stdio())
            .stdout(new_follower_stdio())
            .stderr(new_follower_stdio())
            .spawn()
            .expect("couldn't spawn the new PTY process");

    {
        let mut f = unsafe { File::from_raw_fd(controller_fd) };

        let mut buf = [0; 0x100];
        loop {
            let n = f.read(&mut buf[..])
                .expect("couldn't read");

            if n == 0 {
                break;
            }

            io::stdout().write_all(&buf[..n])
                .expect("couldn't write to STDOUT");
        }
    }

    child.kill()
        .expect("couldn't kill the PTY process");

    child.wait()
        .expect("couldn't wait for the PTY process");
}

这给出以下输出:

Cargo.lock  Cargo.toml  build.Dockerfile  scripts  src  target
thread 'main' panicked at 'couldn't read: Os { code: 5, kind: Uncategorized, message: "Input/output error" }', src/main.rs:35:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

我还尝试使用nix :: unistd :: unistd :: dup重复follower_fd for stdin < /code>,stdoutstderr,但这并没有改变观察到的行为。

作为参考,我正在使用RUST 1.60.0,其中此MWE的以下cargo.toml

[package]
name = "mwe"
version = "0.0.0"

[dependencies]
nix = "=0.24.1"

I'm attempting to read from a process that's backed by a PTY in Rust, but once all bytes have been read from the process then reading from the process returns an Input/output error instead of the expected EOF. Is there an obvious reason for this behaviour, and how might it be resolved so that read returns Ok(0) instead of an error, as per the contract for read?

Here is a minimal working example:

use std::io;
use std::io::Read;
use std::io::Write;
use std::fs::File;
use std::os::unix::io::FromRawFd;
use std::process::Command;
use std::process::Stdio;

extern crate nix;

use crate::nix::pty;
use crate::nix::pty::OpenptyResult;

fn main() {
    let OpenptyResult{master: controller_fd, slave: follower_fd} =
        pty::openpty(None, None)
            .expect("couldn't open a new PTY");

    let new_follower_stdio = || unsafe { Stdio::from_raw_fd(follower_fd) };

    let mut child =
        Command::new("ls")
            .stdin(new_follower_stdio())
            .stdout(new_follower_stdio())
            .stderr(new_follower_stdio())
            .spawn()
            .expect("couldn't spawn the new PTY process");

    {
        let mut f = unsafe { File::from_raw_fd(controller_fd) };

        let mut buf = [0; 0x100];
        loop {
            let n = f.read(&mut buf[..])
                .expect("couldn't read");

            if n == 0 {
                break;
            }

            io::stdout().write_all(&buf[..n])
                .expect("couldn't write to STDOUT");
        }
    }

    child.kill()
        .expect("couldn't kill the PTY process");

    child.wait()
        .expect("couldn't wait for the PTY process");
}

This gives the following output:

Cargo.lock  Cargo.toml  build.Dockerfile  scripts  src  target
thread 'main' panicked at 'couldn't read: Os { code: 5, kind: Uncategorized, message: "Input/output error" }', src/main.rs:35:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I've also tried using nix::unistd::dup to duplicate the follower_fd for stdin, stdout and stderr, but this didn't change the observed behaviour.

For reference, I'm using Rust 1.60.0 with the following Cargo.toml for this MWE:

[package]
name = "mwe"
version = "0.0.0"

[dependencies]
nix = "=0.24.1"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

季末如歌 2025-02-03 01:44:38

看来该错误是Linux上PTY的预期行为,并且基本上是EOF的信号。此信息得到了许多非授权来源的支持,但是一个很好的摘要由 mosvy in unix stackexchange

在linux上,read()在伪tty的主侧将返回-1,并设置errno to eio当关闭其从属侧的所有手柄已关闭时,但是将在首次打开该从设备之前阻止或返回eagain

我不知道是否有任何标准规格或基本原理,但是它允许(粗略)检测另一面关闭何时关闭,并简化了像脚本这样的程序的逻辑它内部的程序。

假定此处描述的eio对应于上面返回的“输入/输出错误”。

It seems that this error is expected behaviour for PTYs on Linux, and essentially signals EOF. This information is supported by a number of non-authoritative sources, but a good summary is provided by mosvy on the Unix StackExchange:

On Linux, a read() on the master side of a pseudo-tty will return -1 and set ERRNO to EIO when all the handles to its slave side have been closed, but will either block or return EAGAIN before the slave has been first opened.

I don't know if there's any standard spec or rationale for this, but it allows to (crudely) detect when the other side was closed, and simplifies the logic of programs like script which are just creating a pty and running another program inside it.

It is presumed that the EIO described here corresponds to the "Input/output error" returned above.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文