C和Rust代码段之间的不同行为



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

然而,这两个代码片段有不同的行为。这些不同行为的根本原因是什么?
C代码段
// 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 cookien"
#define PAR_MESS "Parent: testing...n"
int main() {
int pipe_fd[2] = {-1, -1};    
int len = 0;
char buf[100] = {''};
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代码片段
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 cookien";
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 cookien` 
Two seconds elapsed:   print `Parent: testing...`
Three seconds elapsed: print `Parent: testing...`
...
Five seconds elapsed:  print `Parent: testing...nChild: I wanna cookien`
...
然而,对于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 cookien` 
Two second elapsed:    print `Parent: testing...nChild: I wanna cookien` 
Three seconds elapsed:  print `Parent: testing...nChild: I wanna cookien`
...

每秒钟打印一次Parent: testing...nChild: I wanna cookien

我环境:

$ 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();,它们具有相同的行为。

最新更新