由unix环境高级编程程序清单8-1fork()引发对缓冲的思考

最近刚看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缓冲区里的内容复制了。

 

 

 



你可能感兴趣的:(由unix环境高级编程程序清单8-1fork()引发对缓冲的思考)