gpt4 book ai didi

rust - Rust 可以捕获 C/C++ 库的 stdout/stderr 吗?

转载 作者:行者123 更新时间:2023-12-03 11:26:31 27 4
gpt4 key购买 nike

我将 C/C++ 库包装在 Rust crate 中并使用 FFI 调用它(我没有使用子进程)。
该库记录到 stdout/stderr(例如,使用 printf()std::cout )但我想“捕获”此输出并使用 Rust 的 log crate 来控制输出。
是否可以将 FFI 调用的 stdout/stderr 重定向到 log ?

最佳答案

请在下面找到说明不同的示例
重定向/恢复标准错误(文件描述符 2)的步骤。
这里使用的(C-like)风格是为了保持这个
示例最小;当然,您可以使用 libcstruct 中正确封装和封装所有这些.
请注意,在琐碎的情况下,您可以重复
根据需要多次重定向/调用/获取/恢复序列,
只要你保留pipe_fd , saved_fdlog_file打开。
然而,在非平凡的情况下,隐含了某种复杂性:

  • 如果 C 代码产生很长的消息,我们如何检测
    我们都读完了吗?
  • 我们可以在 STDERR_FILENO 中注入(inject)一个结束标记。之后
    消息在调用步骤生成,然后读取 log_file直到在获取步骤中检测到此标记。 (这增加了
    某种文本处理)
  • 我们可以重新创建管道和log_file在每次重定向之前
    一步,关闭PIPE_WRITE在调用步骤之前结束,阅读log_file直到到达 EOF 并在获取步骤中将其关闭。
    (这增加了更多系统调用的开销)

  • 如果 C 代码产生一个很长的消息,它不会超过
    管道的内部缓冲区容量(然后阻止写入)?
  • 我们可以在单独的线程中执行调用步骤,并且join()在获取步骤完成后(结束标记或
    达到 EOF),因此调用看起来仍然是串行的
    从应用程序的角度来看。
    (这增加了生成/加入线程的开销)
  • 另一种方法是将应用程序的所有日志记录部分
    在一个单独的线程中(全部生成一次)并保留所有
    调用步骤串行。
    (如果应用程序的日志记录部分不必
    被认为是连续的,这没关系,但否则这只是报告
    同样的问题还有一个线程)
  • 我们可以 fork()执行重定向并调用
    子进程中的步骤(如果应用程序数据没有
    要更改,只需阅读),摆脱恢复步骤和wait()获取步骤完成后的流程
    (到达结束标记或 EOF),因此调用仍然
    从应用程序的角度来看是连续的。
    (这增加了产生/等待进程的开销,并且
    排除了更改应用程序数据的能力
    调用代码)


  • // necessary for the redirection
    extern "C" {
    fn pipe(fd: *mut i32) -> i32;
    fn close(fd: i32) -> i32;
    fn dup(fd: i32) -> i32;
    fn dup2(
    old_fd: i32,
    new_fd: i32,
    ) -> i32;
    }
    const PIPE_READ: usize = 0;
    const PIPE_WRITE: usize = 1;
    const STDERR_FILENO: i32 = 2;

    fn main() {
    //
    // duplicate original stderr in order to restore it
    //
    let saved_stderr = unsafe { dup(STDERR_FILENO) };
    if saved_stderr == -1 {
    eprintln!("cannot duplicate stderr");
    return;
    }
    //
    // create resources (pipe + file reading from it)
    //
    let mut pipe_fd = [-1; 2];
    if unsafe { pipe(&mut pipe_fd[0]) } == -1 {
    eprintln!("cannot create pipe");
    return;
    }
    use std::os::unix::io::FromRawFd;
    let mut log_file =
    unsafe { std::fs::File::from_raw_fd(pipe_fd[PIPE_READ]) };
    //
    // redirect stderr to pipe/log_file
    //
    if unsafe { dup2(pipe_fd[PIPE_WRITE], STDERR_FILENO) } == -1 {
    eprintln!("cannot redirect stderr to pipe");
    return;
    }
    //
    // invoke some C code that should write to stderr
    //
    extern "C" {
    fn perror(txt: *const u8);
    }
    unsafe {
    dup(-1); // invalid syscall in order to set errno (used by perror)
    perror(&"something bad happened\0".as_bytes()[0]);
    };
    //
    // obtain the previous message
    //
    use std::io::Read;
    let mut buffer = [0_u8; 100];
    if let Ok(sz) = log_file.read(&mut buffer) {
    println!(
    "message ({} bytes): {:?}",
    sz,
    std::str::from_utf8(&buffer[0..sz]).unwrap(),
    );
    }
    //
    // restore initial stderr
    //
    unsafe { dup2(saved_stderr, STDERR_FILENO) };
    //
    // close resources
    //
    unsafe {
    close(saved_stderr);
    // pipe_fd[PIPE_READ] will be closed by log_file
    close(pipe_fd[PIPE_WRITE]);
    };
    }

    关于rust - Rust 可以捕获 C/C++ 库的 stdout/stderr 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63600029/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com