书中示例代码如下:
#include "apue.h"
int glob=6;
char buf[]="a write to stdout\n";
int main(void)
{
int var;
pid_t pid;
var=88;
if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf) -1)
err_sys("write error");
printf("write error");
if((pid=fork()) < 0)
err_sys("fork error");
else if(pid == 0)
{
glob++;
var++;
}
else
{
sleep(2);
}
printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
exit(0);
}
在终端输出如下:
$ ./a.out
a write to stdout
before fork
pid=430, glob=7, var=89
pid=429, glob=6, var=88
$./a.out >temp.out #输出重定向
a write to stdout
before fork
pid=430, glob=7, var=89
before fork
pid=429, glob=6, var=88
问1:为什么write只输出一次?
答:只进程执行的是fork后的代码,而不是从头开始执行。fork后,子进程共享父进程的数据段、代码段、堆栈段。文件表项属于栈段,所以也被复制了。同样缓冲,也被复制了。但由于write系统调用没有缓冲,所以,子进程复制的缓冲区里,没有此数据,即:buf。所以,子进程输出时,也没有输出此数据。
问2:为什么“befork fork"在第一个命令输出了一次,在第二个命令输出了两次。
答:标准IO是有缓冲的。但标准输出连接到终端时(输出到终端),是行缓冲。否则,是全缓冲。对于第一个命令:标准输出连接到终端,是行缓冲。由于输出中添加了换行符,所以,输出输出到缓冲区后,缓冲区被flush了。即,缓冲区为空。子进程复制的缓冲区的时候,缓冲区为空。所以,子进程输出的,就是在fork之后的printf里的数据。即:
"pid=430, glob=7, var=89\n"
对于第二个命令:标准输出被重定向至文件,此时,便是全缓冲。只有当缓冲区满,或关闭程序的时候才执行写操作,即是把数据输出到文件。子进程在复制父进程的各种数据的时候,在子进程的fork前,”before fork“仍在缓冲区中,子进程变回把缓冲区中的数据,即:”before fork“复制到自己的缓冲区。所以,子进程输出的是,”before fork“和fork之后的printf里的数据。输出的全部数据即是:
"before fork\npid=430, glob=7, var=89\n"
不管在什么情况下,父进程调用了什么printf,就输出对应的数据。不受影响。受这种影响的是子进程。
不管能不能对大家有帮助,但是写下来,防止将来遗忘总是极好的。