fork和缓冲区

  fork在面试中经常被问到,在这里复习一下。

  frok创建子进程,父子进程共享.text段,子进程获得父进程数据段、堆和栈的副本,由于在fork之后经常跟随者exec,所以很多实现并不执行父进程数据段、堆和栈的完全复制,而是使用写时复制(Copy-On-Write,COW)技术。这些区域由父子进程共享,并被内核设为只读,如果父子进程试图修改这些区域,则内核只为修改区域的那块内存制作一个副本。

接下来的程序演示一下fork函数的功能

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>

void err_sys(const char *s)
{
    printf("error:%s",s);
    exit(EXIT_FAILURE);
}

int glob = 6;
char buf[] = "a write to stdout\n";

int main()
{
    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,glob = %d,var = %d\n",getpid(),glob,var);
    exit(0);
}

 编译得到fork_sample,运行结果如下:

当直接执行fork_sample时,结果不出乎意料。在fork前先打印"before fork\n",fork之后父子进程分别打印。

但是当程序输出重定向到temp文件时(即./fork_sample > temp的作用),读取temp文件(即cat temp),我们发现"before fork\n"被输出了两遍,这是为什么呢?

这得从标准I/O库的缓冲说起。

标准I/O库提供了三种类型的缓冲:

(1) 全缓冲:填满标准I/O缓冲区才实际进行I/O操作。

(2)行缓冲:在输入和输出中遇到换行符时,标准I/O库执行I/O操作,当流涉及终端时,通常使用行缓冲。

(3)不带缓冲:标准出错流stderr通常是不带缓冲的。

上面说的缓冲指的是应用层的缓冲,在进行实际的I/O操作时,相关的系统调用(read和write)其实在内核也有缓冲区的。

当直接执行./fork_sample时,由于标准输出时行缓冲的,所以遇到换行符'\n'后缓冲区被冲洗。

当将程序输出重定向到别的文件时,是标准输出是全缓冲的,fork之前printf的数据仍在缓冲区中,在fork时该缓冲区也被复制到子进程中,因此我们就会看到"before fork\n"输出了两次。

posted @ 2016-03-30 20:13  你好呵呵  阅读(900)  评论(0编辑  收藏  举报