• 标准IO的缓冲问题


        在看APU时,第8章进程时,

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 
     4 int globalvar = 6;
     5 char buf[] = "a write to stdout
    ";
     6 
     7 int main ( int argc, char *argv[] )
     8 {
     9     int var;
    10     pid_t pid;
    11 
    12     var = 88;
    13 
    14     if ( write(STDOUT_FILENO, buf, sizeof(buf)-1)  != sizeof(buf)-1)
    15     {
    16         perror("write");
    17         return -1;
    18     }
    19 
    20     printf("before fork
    ");
    21     //fflush(stdout);
    22     if ( ( pid = fork() ) < 0 )
    23     {
    24         perror("fork");
    25         return -1;
    26     }
    27     else if ( pid == 0 )
    28     {
    29         globalvar++;
    30         var++;
    31     }
    32     else
    33     {
    34         sleep(2);
    35     }
    36 
    37     printf("pid = %ld, glob = %d, var = %d
    ", (long)getpid(), globalvar, var);
    38     return 0;
    39 } 

    编译执行程序,得到:

    thomas@thomas-laptop:~/test/apu$ ./a
    a write to stdout before fork pid
    = 3210, glob = 7, var = 89 pid = 3209, glob = 6, var = 88

    换一个方式执行,将程序执行结果重定向到一个文件:

    thomas@thomas-laptop:~/test/apu$ ./a > out
    thomas@thomas-laptop:~/test/apu$ cat out
    a write to stdout
    before fork
    pid = 3312, glob = 7, var = 89
    before fork
    pid = 3311, glob = 6, var = 88

    发现前后2此结果不一样,第一个printf输出了2次,这是因为printf是属于标准IO,他的输出是带缓冲的,程序上面使用write进行输出却不会这样,write是不带缓冲的。

    缓冲的目的是尽量减少对write read的调用,以达到提高IO效率的目的,比如连续的多个printf,如果不带缓冲,势必每调用一次printf就得进行一次系统调用,调用wirte(STDOUT_FILENO,buf, sizeof(buf)),如果申请一块内存,先将printf的输出内容存放在这块内存里面,等到这块内存满了再调用write进行输出,这么一来就大大减少了系统调用。

      标准I/O提供了三种类型的缓冲:
    1、全缓冲。这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常是由标准I/O库实施全缓冲。一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc获得需使用的缓冲区
    术语冲洗说明I/O缓冲区的写操作。缓冲区可由标准I/O例程自动冲洗,或者可以调用函数fflush冲洗一个流。值得引起注意的是在UNIX环境 中,flush有两种意思。在标准I/O库方面,flush意味着将缓冲区中的内容写到磁盘上。在终端驱动程序方面flush表示丢弃已存储在缓冲区中的数据。
    2、行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。当流涉及一个终端时,通常使用行缓冲。
    对于行缓冲有两个限制。第一,因为标准I/O库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,那么即使没有写一个换行符,也进行I/O操 作。第二,任何时候只要通过标准I/O库要求从a一个不带缓存的流,或者b一个行缓冲的流得到输入数据,那么就会造成冲洗所有行缓冲输出流。在b中带了一 个在括号中的说明,其理由是,所需的数据可能已在缓冲区中,他并不需求在需要数据时才从内核读数据。很明显,从不带缓冲的一个流中进行输入要求当时从内核得到数据。
    3、不带缓冲。标准I/O库不对字符进行缓冲存储。例如,如果用I/O函数fputs写15个字符到不带缓冲的流中,则该函数很可能用write系统调用函数将这些字符立即写至相关联的打开文件中。
    标准出错流stderr通常是不带缓冲的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个换行符。

      执行./a时,由于是在一个shell终端中执行,所以printf是行缓冲,这样一来遇到了‘ ’就立即输出了,但是如果使用./a > out进行重定向,此时是上面蓝色字体那种情况,所有执行的是全缓冲,明显它的输出肯定还不至于填满缓冲区,所以fork前并不会输出,而是缓冲到了标准IO库malloc出来的一片内存里面,经过fork,这片区域在子进程里面生成了一个副本(fork会将父进程的堆栈区,数据段,bss段进行复制,生成拷贝,它们只共享代码段,而mallc位于堆上),等到程序执行结束时,全缓冲才输出,这样一来就出现了2个"before fork"。

  • 相关阅读:
    hdu 2199 Can you solve this equation? 二分
    STL 学习代码~
    hdu 1551 Cable master 二分
    fzu 1564 Combination 组合数是否包含因数
    fafu 1079 帮冬儿忙 组合数包含因数个数
    soj 3290 Distribute The Apples I 组合数对素数取余
    fzu 2020 组合 组合数对素数取余
    hdu 1969 Pie 二分
    hdu 2141 Can you find it? 二分
    hdu 2899 Strange fuction 二分
  • 原文地址:https://www.cnblogs.com/thammer/p/4982448.html
Copyright © 2020-2023  润新知