1个不稳定版本

0.1.0 2024年7月3日

#311 in Unix API

Download history 127/week @ 2024-07-01 61/week @ 2024-07-08 39/week @ 2024-07-15 20/week @ 2024-07-22 30/week @ 2024-07-29

156 每月下载量
10 个crate中使用(通过swayipc-async

MIT/Apache

18KB
174

这是async-pidfd的维护分支。

Linux的进程文件描述符(pidfd

进程文件描述符(pidfd)为Linux上的进程管理提供了一种无竞争的方法,通过文件描述符而不是进程ID(PID)来保持对进程的持续引用,进程退出后PID可能会被重用。

此crate仅适用于Linux;如果您需要其他平台的支持或旧版Linux内核的支持,请参阅async-process

async-pidfd为pidfd提供Rust支持,并支持同步(通过PidFd类型)和异步(通过AsyncPidFd类型)管理进程。

同步 - PidFd

PidFd类型同步管理进程。使用PidFd::from_pid从进程ID构建PidFd,例如从标准库中的Child::id。请注意,可移植的Child::id函数返回进程ID为u32,而不是libc::pid_t,因此需要进行类型转换。

use std::os::unix::process::ExitStatusExt;
use std::process::{Command, ExitStatus};

use async_pidfd::PidFd;

fn main() -> std::io::Result<()> {
    let child = Command::new("/bin/true").spawn()?;
    let pidfd = PidFd::from_pid(child.id() as libc::pid_t)?;
    let status = pidfd.wait()?.status();
    assert_eq!(status.code(), Some(0));

    let child = Command::new("/bin/sh").arg("-c").arg("kill -9 $$").spawn()?;
    let pidfd = PidFd::from_pid(child.id() as libc::pid_t)?;
    let status = pidfd.wait()?.status();
    assert_eq!(status.signal(), Some(9));

    Ok(())
}

PidFd::wait 通过 ExitInfo 结构体返回有关已退出进程的信息。 ExitInfo 包含一个 libc::siginfo_t,指示进程如何退出(包括如果它正常退出,则包括退出代码,如果它被信号终止,则包括信号),以及一个 libc::rusage,描述进程及其子进程的资源使用情况。 libc::siginfo_t 具有复杂的语义;要获取 std::process::ExitStatus,您可以在 ExitInfo 上调用 .status()

请注意,虽然为任意进程打开 PID 可能会与该进程的退出发生竞争,但打开尚未等待的子进程的 PID 是安全的,因为进程 ID 会在您等待进程(或阻塞 SIGCHLD)之前不会重用。

如果您只想使用同步的 PidFd 类型,您可以在 Cargo.toml 中使用 async-pidfddefault-features = false 来删除与异步相关的依赖项。

异步 - AsyncPidFd

AsyncPidFd 类型根据 Stjepan Glavina 的 async-io crate 异步管理进程。 async-io 提供了一个 Async 包装器,它可以将任何基于文件描述符的同步类型轻松地转换为异步类型;生成的异步代码使用 epoll 并发地等待所有文件描述符。

AsyncPidFd 包装了一个 Async<PidFd> 并提供了与 PidFd::wait 相同的 API,但提供了异步版本的 wait 函数。

use std::os::unix::process::ExitStatusExt;
use std::process::{Command, ExitStatus};

use async_pidfd::AsyncPidFd;
use futures_lite::future;

async fn async_spawn_and_status(cmd: &mut Command) -> std::io::Result<ExitStatus> {
    let child = cmd.spawn()?;
    let pidfd = AsyncPidFd::from_pid(child.id() as libc::pid_t)?;
    Ok(pidfd.wait().await?.status())
}

fn main() -> std::io::Result<()> {
    future::block_on(async {
        let (status1, status2) = future::try_join(
            async_spawn_and_status(&mut Command::new("/bin/true")),
            async_spawn_and_status(&mut Command::new("/bin/false")),
        )
        .await?;
        assert_eq!(status1.code(), Some(0));
        assert_eq!(status2.code(), Some(1));
        Ok(())
    })
}

依赖项

~0–8MB
~71K SLoC