为何UNIX中未调用wait()的父进程总晚于子进程执行?
为何未调用wait(),文件内容却始终为"ba"?
问题背景
作业题
Q7:执行以下代码后会生成名为myFile.txt的新文件,该文件的内容是否一致?为什么?
代码片段
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/wait.h> int main(int argc, char *argv[]){ printf("hello world (pid:%d)\n", (int)getpid()); int fd = open("myFile.txt", O_CREAT|O_RDWR); if(fd == -1 ) { printf("Unable to open the file\n exiting....\n"); return 0; } int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) { printf("hello, I am child (pid:%d)\n", (int)getpid()); char myChar='a'; write(fd, &myChar,1); printf("writing a character to the file from child\n"); } else { printf("hello, I am parent of %d (pid:%d)\n", rc, (int)getpid()); char myChar='b'; write(fd, &myChar,1); printf("writing a character to the file from parent\n"); } return 0; }
实际观察与疑问
实际运行时,父进程写入字符'b',子进程写入字符'a'。多次执行程序后发现文件内容始终为"ba"——即父进程的写入总是先于子进程,但代码中父进程并未调用wait()函数来等待子进程,恳请解释其中原因。
核心原因分析
1. 进程调度与操作执行的异步性
fork()调用完成后,内核会自主决定先调度父进程还是子进程,这本身是不确定的,但在低负载环境中,子进程更频繁地被优先调度,导致子进程的printf输出先出现在终端。但要注意:
printf(终端I/O)和write(文件I/O)是两个独立的系统调用,进程间没有任何同步机制约束它们的执行顺序。- 子进程执行完
printf后,可能因为终端I/O的延迟被内核切换到父进程,父进程此时快速完成write操作(文件写入利用页缓存,速度远快于终端输出),之后才切回子进程执行它的write。这就会出现“子进程先打印,但父进程先写入文件”的现象。
2. 特定环境下的调度规律性
虽然理论上进程调度是随机的,但在负载较低的系统中,内核的调度行为会呈现出一定的规律:
fork()会复制父进程的资源,内核为了避免父进程继续执行干扰子进程的资源复制工作,通常会优先调度子进程启动。- 但子进程的
printf涉及终端交互,终端I/O的处理延迟相对较高,内核会在此时切换回父进程执行;而父进程的write操作仅需写入内存页缓存,几乎没有延迟,因此父进程的write会先完成。 - 这种规律在相同环境、低负载下会重复出现,导致你每次运行都看到文件内容为
"ba"。
3. 共享文件描述符的影响
由于open是在fork之前调用的,父子进程共享同一个文件描述符对应的文件表项,其中包括共享的文件偏移量:
- 无论哪个进程先执行
write,都会将文件偏移量向后移动1位;另一个进程的write会接着写入下一个位置,不会出现内容覆盖。 - 文件内容为
"ba",说明父进程的write总是先修改了文件偏移量,因此'b'被写入文件起始位置,'a'被写入第二个位置。
4. 为何无需wait()也有稳定结果?
wait()的作用是强制父进程等待子进程退出,保证子进程的操作全部完成后父进程再继续执行。但你的场景中,没有wait()却出现稳定结果,只是特定环境下的巧合:
- 这种固定顺序不具备跨系统通用性,如果在高负载系统或采用不同调度算法的内核中运行,完全可能出现文件内容为
"ab"的情况。
内容的提问来源于stack exchange,提问作者yanzhen huang
相关产品推荐
相关产品推荐

