我有一个C代码片段,它使用pipe()fork()在父进程和子进程之间进行通信.我想在 rust 迹中复制它.对于C中使用的POSIX API,如果Rust标准库中有对应的API,则首选它们.

然而,这两个代码段有不同的行为.这些不同行为的根本原因是什么?

C code snippet
// error handling is omitted for simplicity's sake

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define CHILD_MESS "Child: I wanna cookie\n"
#define PAR_MESS "Parent: testing...\n"

int main() {
    int pipe_fd[2] = {-1, -1};    
    int len = 0;
    char buf[100] = {'\0'};
    int read_len = 0;
    
    pipe(pipe_fd);
    
    switch (fork()) {
        case 0:  // in the child
            len = strlen(CHILD_MESS);
            while(1) {
                write(pipe_fd[1], CHILD_MESS, len);
                sleep(5);
            }
            break; 
        default:  // in the parent
            len = strlen(PAR_MESS);
            while(1) {
                write(pipe_fd[1], PAR_MESS, len);
                sleep(1);
                read_len = read(pipe_fd[0], buf, 100);
                if (read_len <= 0) {
                    break;
                }
                write(1, buf, read_len);
            }
    }
    return 0;
}
Rust code snippet
use nix::unistd::{fork, pipe, ForkResult};  // needs extern crate `nix`
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{FromRawFd, RawFd};
use std::thread::sleep;
use std::time::Duration;

const CHILD_MSG: &str = "Child: I wanna cookie\n";
const PAR_MSG: &str = "Parent: testing...\n";

fn main() {
    let (read_end, write_end): (RawFd, RawFd) = pipe().unwrap();
    let mut buf: [u8; 100] = [0; 100];
    let mut read_end: File = unsafe { File::from_raw_fd(read_end) };
    let mut write_end: File = unsafe { File::from_raw_fd(write_end) };

    match unsafe { fork() } {
        Ok(res) => match res {
            ForkResult::Child => loop {
                write_end.write_all(CHILD_MSG.as_bytes()).expect("write");
                sleep(Duration::from_secs(5));
            },
            ForkResult::Parent { child: _ } => loop {
                write_end.write_all(PAR_MSG.as_bytes()).expect("write");
                sleep(Duration::from_secs(1));
                let n = read_end.read(&mut buf).unwrap();
                if n == 0 {
                    break;
                }
                print!("{}", std::str::from_utf8(&buf).unwrap());
            },
        },
        _ => (),
    }
}

预期的行为类似于:

$ gcc main.c && ./a.out
Parent: testing...
Child: I wanna cookie
Parent: testing...
Parent: testing...
Parent: testing...
Parent: testing...    // Five seconds elapsed
Child: I wanna cookie
Parent: testing...
...
After execution:
One second elapsed:    print `Parent: testing...\nChild: I wanna cookie\n` 
Two seconds elapsed:   print `Parent: testing...`
Three seconds elapsed: print `Parent: testing...`
...
Five seconds elapsed:  print `Parent: testing...\nChild: I wanna cookie\n`
...

然而,对于Rust代码片段,我得到了如下内容:

$ cargo run -q
Parent: testing...
Child: I wanna cookie
Parent: testing...
Child: I wanna cookie
...
After execution:
One second elapsed:    print `Parent: testing...\nChild: I wanna cookie\n` 
Two second elapsed:    print `Parent: testing...\nChild: I wanna cookie\n` 
Three seconds elapsed:  print `Parent: testing...\nChild: I wanna cookie\n`
...

它每秒只打印Parent: testing...\nChild: I wanna cookie\n

我的环境:

$ uname -a
Linux pop-os 5.17.5-76051705-generic #202204271406~1651504840~22.04~63e51bd SMP PREEMPT Mon May 2 15: x86_64 x86_64 x86_64 GNU/Linux
$ ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3) 2.35
$ rustc --version
rustc 1.60.0 (7737e0b5c 2022-04-04)

推荐答案

它们之间的区别在于,print!("{}", std::str::from_utf8(&buf).unwrap());会将整个buf刷新到标准输出,而write(1, buf, read_len);只会写入read_len字节.

如果我们将C中的write(1, buf, read_len);改为write(1, buf, 100);,或将 rust 中的stdout().write(&buf[0..n]).unwrap();改为stdout().write(&buf[0..n]).unwrap();,它们的行为是相同的.

C++相关问答推荐

这是一个合法的C Strdup函数吗?

如何调试LD_PRELOAD库中的构造函数?

具有交换链获取和命令缓冲区提交的同步-危险-读后写错误

Flose()在Docker容器中抛出段错误

为什么双精度d=flt_max+flt_max;在c语言中得到inf的结果

如何使用_newindex数组我总是得到错误的参数

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

在vfork()之后,链接器如何在不 destruct 父内存的情况下解析execve()?

按长度对argv中的单词进行排序

如何在不使用字符串的情况下在c中编写函数atof().h>;

C-try 将整数和 struct 数组存储到二进制文件中

Malloc和对齐

Tcl_GetDoubleFromObj在列表的迭代中是一个缺点

%g浮点表示的最大字符串长度是多少?

C 语言中 CORDIC 对数的问题

使用复合文字数组初始化的指针数组

变量的指针右对齐,函数的指针左对齐

C11 嵌套泛型

创建 makefile 来编译位于不同目录中的多个源文件

对 getchar 函数和 EOF 感到困惑