Rust C/C&x2B的Can标准输出/标准输出+;图书馆会生锈吗?

Rust C/C&x2B的Can标准输出/标准输出+;图书馆会生锈吗?,rust,Rust,我将C/C++库包装在一个生锈的板条箱中,并使用FFI调用它(我没有使用子流程) 这个库记录到stdout/stderr(比如使用printf()或std::cout),但我想“捕获”这个输出,并使用Rust的log板条箱来控制输出 是否可以将FFI调用的stdout/stderr重定向到log?请在下面找到一个示例,说明不同的 重定向/恢复stderr(文件描述符2)的步骤 这里使用的(C-like)样式是为了保持这一点 示例最少;当然,您可以使用libc 将所有这些包装并正确封装在结构中 注

我将C/C++库包装在一个生锈的板条箱中,并使用FFI调用它(我没有使用子流程)

这个库记录到stdout/stderr(比如使用
printf()
std::cout
),但我想“捕获”这个输出,并使用Rust的
log
板条箱来控制输出


是否可以将FFI调用的stdout/stderr重定向到
log

请在下面找到一个示例,说明不同的 重定向/恢复stderr(文件描述符2)的步骤

这里使用的(C-like)样式是为了保持这一点 示例最少;当然,您可以使用
libc
将所有这些包装并正确封装在
结构中

注意,在一般情况下,您可以重复 根据需要多次重定向/调用/获取/还原序列, 如果您保持
pipe\u fd
saved\u fd
log\u文件
打开

然而,在非轻微的情况下,暗示了某种并发症:

  • 如果C代码生成一条很长的消息,我们如何检测 我们都读过了吗?
    • 我们可以在 在调用步骤生成消息,然后读取
      log\u文件
      直到在获取步骤中检测到此标记。(这增加了 某种文本处理)
    • 我们可以在每次重定向之前重新创建管道和日志文件 步骤,关闭
      管道\u WRITE
      在调用步骤之前结束,读取
      log_文件
      直到达到EOF,并在获取步骤中关闭它。 (这增加了更多系统调用的开销)
  • 如果C代码生成一条很长的消息,它不会超过 管道的内部缓冲区容量(然后是块写入)?
    • 我们可以在单独的线程中执行invoke步骤
      join()
      在获取步骤完成(结束标记或 已达到EOF),因此调用看起来仍然是串行的 从应用程序的角度来看。 (这增加了生成/加入线程的开销)
    • 另一种方法是将所有日志记录部分放在应用程序中 在一个单独的线程中(一次性生成),并保留所有 调用步骤是串行的。 (如果应用程序的日志记录部分不必 被认为是串行的,这是可以的,但这只是报告 同样的问题(另一个线程)
    • 我们可以
      fork()
      执行重定向和调用 子流程中的步骤(如果应用程序数据没有 要进行更改,只需读取),取消恢复步骤,然后
      wait()
      获取步骤完成后的过程 (到达结束标记或EOF),以便调用仍然有效 从应用程序的角度看是串行的。 (这增加了生成/等待进程的开销,以及 排除了从数据库更改应用程序数据的能力 调用的代码)
//重定向所必需的
外部“C”{
fn管道(fd:*mut i32)->i32;
fn关闭(fd:i32)->i32;
fn-dup(fd:i32)->i32;
fn dup2(
old_fd:i32,
新(u fd):i32,,
)->i32;
}
常数管道读数:usize=0;
常量管道写入:usize=1;
常量标准文件号:i32=2;
fn main(){
//
//复制原始stderr以恢复它
//
让saved_stderr=unsafe{dup(stderr_FILENO)};
如果保存,\u stderr==-1{
eprintln!(“无法复制标准”);
返回;
}
//
//创建资源(管道+从中读取文件)
//
让mut pipe_fd=[-1;2];
如果不安全{pipe(&mut pipe_fd[0])}==-1{
eprintln!(“无法创建管道”);
返回;
}
使用std::os::unix::io::FromRawFd;
让mut记录文件=
不安全的{std::fs::File::from_raw_fd(pipe_fd[pipe_READ]);
//
//将stderr重定向到管道/日志文件
//
如果不安全{dup2(pipe_fd[pipe_WRITE],STDERR_FILENO)}=-1{
eprintln!(“无法将stderr重定向到管道”);
返回;
}
//
//调用一些应该写入stderr的C代码
//
外部“C”{
fn perror(txt:*常数u8);
}
不安全{
dup(-1);//设置errno的系统调用无效(由perror使用)
perror(&“发生了不好的事情\0”。as_bytes()[0]);
};
//
//获取上一条消息
//
使用std::io::Read;
设mut buffer=[0_u8;100];
如果让Ok(sz)=log_file.read(&mut buffer){
普林顿(
“消息({}字节):{:?}”,
sz,
std::str::from_utf8(&buffer[0..sz]).unwrap(),
);
}
//
//恢复初始标准
//
不安全{dup2(已保存的stderr,stderr\u文件号)};
//
//密切资源
//
不安全{
关闭(已保存);
//管道fd[管道读取]将由日志文件关闭
关闭(管道fd[管道写入]);
};
}

完全阻止任何输出到stdout/stderr,您可以吗?可以,但您可能需要使用不安全和特定于操作系统的代码。例如,在类似Unix的OS es上,您可以使用
dup2()
将标准输出重定向到管道,从后台线程读取该管道,并根据需要调用
log
;我想让生锈的部分“管理”伐木。。。重定向stdout(使用say
dup2()
)是否也会将其更改为锈代码?我会说是的…是的,但你可以在FFI代码完成后撤销效果。(撤销它是相当便宜的,可以归结为几个系统调用。)您还必须注意Rust日志记录将输出延迟到黑客撤销为止,否则它可能会进入无限循环。如果有大量输出,当管道缓冲区填充时,这种方法将挂起。为了避免这种情况,你需要让阅读和写作同时出现在一个单独的线程中。@MichaelAnderson我同意。我的意图是保持示例的简单性,以便将重点放在重要性上