我将C / C ++库包装在Rust板条箱中,并使用FFI对其进行调用(我使用子进程 not )。
此库记录到stdout / stderr(使用printf()
或std::cout
),但我想“捕获”此输出并使用Rust的log
板条箱来控制输出
是否可以将FFI调用的stdout / stderr重定向到log
?
答案 0 :(得分:2)
请在下面找到说明不同之处的示例 重定向/还原stderr(文件描述符2)的步骤。
此处使用的(类似C的)样式旨在保留该样式
最小的例子;当然,您可能可以使用libc
将所有这些东西正确包装并封装在struct
中。
请注意,在平凡的情况下,您可以重复
您可以多次重定向/调用/获取/恢复序列,
前提是您保持pipe_fd
,saved_fd
和log_file
打开。
但是,在非平凡的情况下,意味着某种并发症:
STDERR_FILENO
之后插入结束标记
消息在调用步骤中产生,然后读取log_file
直到在获取步骤中检测到此标记。 (这增加了
某种文本处理)log_file
步骤,在 invoke 步骤之前关闭PIPE_WRITE
端,
log_file
直到达到EOF,然后在获取步骤中将其关闭。
(这会增加更多系统调用的开销)join()
(结束标记或
到达EOF),因此调用仍然看起来是串行的
从应用程序的角度来看。
(这会增加产生/加入线程的开销)fork()
执行重定向和调用
子进程中的步骤(如果应用程序数据中没有
要更改,只需阅读),摆脱 restore 步骤,
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]);
};
}