zoukankan      html  css  js  c++  java
  • 标准I/O的缓冲

    标准I/O实现了三种类型的用户缓冲,并为开发者提供了接口,可以控制缓冲区类型和大小。

    • 无缓冲(Unbuffered)

        不执行用户缓冲。数据直接提交给内核。因为这种无缓冲模式不支持用户缓冲(用户缓冲一般会带来很多好处),通常很少使用,只有一个例外:标准错误默认是采用无缓冲模式。

    • 行缓冲(Line-buffered)

        缓冲是以行为单位执行。每遇到换行符,缓冲区就会被提交给内核。行缓冲对把流输出到屏幕时很有用,因为输出到屏幕的消息也是通过换行符分隔的。因此,行缓冲是终端的默认缓冲模式,比如标准输出。

    • 块缓冲(Block-buffered) 或 完全缓冲(full buffered)

        缓冲以块为单位执行,每个块是固定的字节数。很适用与处理文件。默认情况下,和文件相关的所有流都是块缓冲模式。

    大部分情况下,默认的缓冲模式对于特定场景是最高效的。但是,标准 I/O 还是提供了一个接口,可以修改使用的缓冲模式:

    #include <stdio.h>
    
    int setvbuf(FILE *stream, char *buf, int mode, size_t size);

    mode 值必须是以下之一:

    _IONBF : 无缓冲

    _IOLBF : 行缓冲

    _IOFBF : 块缓冲

    在 _IONBF 无缓冲模式下,会忽略参数 buf 和 size;对于其他模式,buf 可以指向一个 size 字节大小的缓冲空间,标准 I/O 会用它来执行对给定流的缓冲。如果 buf 为空,glibc 会自动分配指定 size 的缓冲区。

    setvbuf() 函数必须在打开流后,并在执行任何操作之前被调用。

    行缓冲:

    1 #include <stdio.h>
    2 
    3 int main(int argc, char* argv[])
    4 {
    5         printf("hello world");
    6         while(1);
    7         return 0;
    8 }

    执行程序,看到没有输出“hello world”

    如果在“hello world”后面加上“ ”,可以看到输出了“hello world”

    说明标准输出的缺省模式是行缓冲

    行缓冲大小:

     1 #include <stdio.h>
     2 
     3 int main(int argc, char* argv[])
     4 {
     5         int i = 0;
     6         while(i < 1024)
     7         {
     8                 printf("h");
     9                 i++;
    10         }
    11         while(1);
    12         return 0;
    13 }

    如果把1024改成1025,就能看到输出,说明行缓冲的大小是1024字节

    无缓冲:

    1 #include <stdio.h>
    2 
    3 int main(int argc, char* argv[])
    4 {
    5         fprintf(stderr, "hello world");
    6         while(1);
    7         return 0;
    8 }

    可以看到“hello world”输出了,表明stderr是无缓冲的

    1 #include <stdio.h>
    2 
    3 int main(int argc, char* argv[])
    4 {
    5         setvbuf(stdout, NULL, _IONBF, 0);
    6         fprintf(stdout, "hello world");
    7         while(1);
    8         return 0;
    9 }

    可以把 stdout 设置为无缓冲

     1 #include <stdio.h>
     2 
     3 char buf[2046];
     4 
     5 int main(int argc, char* argv[])
     6 {
     7         setvbuf(stdout, buf, _IOLBF, 2046);
     8         int i = 0;
     9         while(i < 2047)
    10         {
    11                 printf("h");
    12                 i++;
    13         }
    14         while(1);
    15         return 0;
    16 }

    可以改变缓冲区大小

    行缓冲的输出条件:遇到换行符“ ”;缓冲区满

     1 #include <stdio.h>
     2 
     3 char buf[2046];
     4 
     5 int main(int argc, char* argv[])
     6 {
     7         setvbuf(stdout, buf, _IOLBF, 2046);
     8         int i = 0;
     9         while(i < 2046)
    10         {
    11                 printf("h");
    12                 if (i == 2000) printf("
    ");
    13                 i++;
    14         }
    15         while(1);
    16         return 0;
    17 }

    在关闭流时,其使用的缓冲区必须存在。一个常见的错误就是把缓冲区定义成某一个作用域中的局部变量,在关闭流之前就退出这个作用域了。

     1 #include <stdio.h>
     2 
     3 int main(int argc, char* argv[])
     4 {
     5         char buf[BUFSIZ];
     6         setvbuf(stdout, buf, _IOFBF, BUFSIZ);
     7         printf("Arrr!
    ");
     8         return 0;
     9         /* 'buf' exits scope and is freed, but stdout isn't closed until later */
    10 }

    这类错误可以通过两种方式解决:一是在离开作用域之前显示关闭流,二是把 buf 设置为全局变量

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <string.h>
     4 #define BUF_SIZE 10
     5 char buf[BUF_SIZE];
     6 
     7 int myprintf(char* str)
     8 {
     9         if (NULL == str) return;
    10         char *cur = str;
    11         int len = 0;
    12         int have_n = 0;
    13         while(*cur != '') {
    14                 len++;
    15                 cur++;
    16                 if (*cur == '
    ') have_n = 1;
    17         }
    18 
    19         int buf_len = strlen(buf);
    20         int len_sum = buf_len + len;
    21 
    22         if (len_sum >= BUF_SIZE - 1 || have_n) {
    23                 write(STDOUT_FILENO, buf, buf_len);
    24                 memset(buf, 0, BUF_SIZE);
    25                 write(STDOUT_FILENO, str, len);
    26         }
    27         else {
    28                 strncpy(buf, str, len);
    29                 buf[BUF_SIZE] = '';
    30         }
    31         return 0;
    32 }
    33 
    34 int main(int argc, char* argv[])
    35 {
    36         myprintf("1234
    5678");
    37         //myprintf("9");
    38         return 0;
    39 }

    标准 I/O 函数在本质上是线程安全的。在每个函数的内部实现中,都关联了一把锁、一个锁计数器,以及持有该锁并打开一个流的线程。每个线程在执行任何 I/O 请求之前,必须首先获得锁而且持有该锁。两个或多个运行在同一流上的线程不会交叉执行标准 I/O 操作,因此,在单个函数调用中,标准 I/O 操作是原子操作。

  • 相关阅读:
    1分钟快速生成用于网页内容提取的xslt
    Python即时网络爬虫项目: 内容提取器的定义
    Python读取PDF内容
    Golang基础(二)
    shell的sed命令
    matplotlib + pandas绘图
    关于字符编码:ascii、unicode与utf-8
    shell的sort命令
    shell的uniq命令
    shell的tr命令
  • 原文地址:https://www.cnblogs.com/jingyg/p/5343978.html
Copyright © 2011-2022 走看看