最近刚看unix环境高级编程这本书,对于缓冲的概念总是比较模糊,当看到这段程序程序的时候,就细细的琢磨了一番,终于对于缓冲的概念有了一个新的认识,并且也加深了对于fork()的理解。
首先看下这段程序173.c
#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("before fork \n"); if((pid=fork())<0) err_sys("fork error"); else if(pid==0) { glob++; var++; } else sleep(2); printf("pid= %d,glod=%d, var=%d \n",getpid(),glob,var); exit(0); }
学过linux就应该知道,fork()之后,是父进程先执行还是子进程先执行取决于内核的调度算法,所以在这为了先让子进程执行,我们让父进程睡眠2秒,虽然这不一定保证能达到子进程先执行的目的。但是我在ubuntu12.04验证过了,这是可以保证的。
当我们用命令cc 173.c编译后再用命令./a.out执行的时候,输出如下:
a write to stdout
before fork
pid= 3196,glod=7, var=89
pid= 3195,glod=6, var=88
由于write函数是不带缓冲的,因为在fork()之前调用的write,所以其数据写到标准输出一次。
但是标准I/O库是带缓冲的,当我们用以上这种方式运行的时候,before fork只会输出一次,其原因是我们是用的交互式方式运行的该程序,且标准输出连到终端设备,它是行缓冲,标准缓冲区由换行符冲洗掉了,所以只得到printf一次。
接下来我们用第二种方式运行该程序,执行命令./a.out > temp.out,然后cat temp.out,得到结果如下:
a write to stdout
before fork
pid= 3205,glod=7, var=89
before fork
pid= 3204,glod=6, var=88
我们得到两次before fork,这是因为我们把标准输出重定向到了temp.out,它是全缓冲的,那么该行数据不会被换行符冲洗掉,然后在将父进程的数据空间复制到子进程中时,该缓冲区也被复制到了子进程中。于是父子进程都拥有该行内容的标准I/O缓冲区。
为了验证这一点,我改写了一下程序,如下:
#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("before fork %d\n",getpid()); if((pid=fork())<0) err_sys("fork error"); else if(pid==0) { glob++; var++; } else sleep(2); printf("pid= %d,glod=%d, var=%d \n",getpid(),glob,var); exit(0); }
重新编译后输出如下:
a write to stdout
before fork 3204
pid= 3205,glod=7, var=89
before fork 3204
pid= 3204,glod=6, var=88
两次输出的都是before fork 3204,而3204是父进程的pid.所以子进程是把父进程的标准I/O缓冲区里的内容复制了。