Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7861
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 95
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-15 14:37
文章分类

全部博文(15)

文章存档

2016年(2)

2015年(13)

我的朋友

分类: LINUX

2016-07-29 17:06:24

在APUE中的fork章节,有个实例,之前看半天不理解,现在讲解一下
例程:

点击(此处)折叠或打开

  1. #include
  2. #include
  3. #include

  4. int globVar = 6;
  5. char buf[] = "a write to stdout\n";
  6. int main(void)
  7. {
  8.     int var;
  9.     pid_t pid;

  10.     var = 88;
  11.     if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) {
  12.         perror("write");
  13.         exit(-1);
  14.     }
  15.     printf("before fork\n");

  16.     if ( (pid = fork()) < 0 ) {
  17.         perror("fork");
  18.         exit(-1);
  19.     }
  20.     else if ( 0 == pid ) { //chlid
  21.         globVar++;
  22.         var++;
  23.     }
  24.     else { //parent
  25.         sleep(2);
  26.     }

  27.     printf("pid=[%ld], globVar=[%d], var=[%d]\n", (long)getpid(), globVar, var);

  28.     exit(0);
  29. }
执行程序得到:
$ ./a.out 
a write to stdout
before fork
pid=[7285], globVar=[7], var=[89]
pid=[7284], globVar=[6], var=[88]
$ ./a.out  > temp.out
$ cat temp.out 
a write to stdout
before fork
pid=[7287], globVar=[7], var=[89]
before fork
pid=[7286], globVar=[6], var=[88]

注意点1:
改程序fork与I/O函数之间的交互关系。
文中提了几点:
1 write函数是不带缓冲的。因为在fork之间调用write,所以其数据写到标准输出一次。但是,标准I/O库是带缓冲的。
2 如果标准输出链接的是终端设备,则是行缓冲(换行符);否则是全缓冲。
所以当以交互方式运行该程序时,只得到该printf输出的行 一次,其原因是标准输出缓冲区由换行符冲洗。
但当将标准输出重定向到一个文件时,却得到printf输出行 两次,其原因是在fork之前调用了printf一次,但当调用fork时,该行数据仍在缓冲区中,然后再讲父进程数据空间复制到子进程中时,该缓冲区数据也被复制到子进程中,此时 父进程和子进程各自有了带该行内容的缓冲区。在exit之前的第二个printf将其数据追加到已有的缓冲区中。当每个进程终止时,其缓冲区中的内容都被写到相应文件中。

这个是官方的版本,其实如果你明白后反过来看其实还是说的很清楚的,但如果第一次看,再加上对翻译人员的表述方式不适应的话,很可能看晕。
所以,下面是简单的大白话版本:
同样首先是write的问题,目的地是标准I/O,也就是个池子,带缓冲的。write就是个进入池子的方式,方式本身不带缓冲。可以看做是缓冲区的输入。
其次,数据已经进入池子了,此时,缓冲区的输出的也要分目的地:
    如果是终端设备的交互方式,则为行缓冲,也就是一行1024个字节,遇换行符就输出。
    如果是其它地方,这里是文件,则为全缓冲,全缓冲也就是4096个字节。
P.S. : 这个1024byte和4096byte怎么来的?可以参见APUE的5.12章节

接下来说重点了,那么问题来了:
    首先fork的时候,子进程才会复制父进程的一些东西。显然before这行是在fork之前,理论上是不会复制printf这个调用本身的,那为什么输出为文件的时候会有两个before呢?
    第一次打印只有一个before的原因是,因为是终端所以是行缓冲,before这行的printf中是有\n的,所以既然是行缓冲,理所应当输出的终端,所以大家看到的这个before出现的运行时的时刻是执行完printf后直接输出的。
    第二次打印在子进程整体打印完后的父进程又打印一遍的原因是,正如问题描述的本身所讲,其实此时的before并不是printf这个调用本身的,而是因为输出的目的地不是终端所以为全缓冲,也就是before这行打印包括换行符一起还在缓冲区的池子中,并没有输出出来,所以之后运行到fork的时候,子进程复制父进程的时候,当然缓冲区中的before也被复制了一份,所以大家从文件里看到的before并不是在fork之前的printf结束后立即输出的,而是在每个进程终止时,缓冲区冲洗的时候父子进程分别输出的。
阅读(364) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~