GNU/Linux C 库I/O缓冲机制

简介: GNU/Linux C 库I/O缓冲机制

0.引子


对于使用C语言开发者着而言,I/O库可能是最为经常的使用的程序库,当你需要调试的时候可能最为经常使用的就是printf打印了。可是不知道大家,是不是遇到过这样一个问题,那就是我明明打印了,为什么终端里没有输出呢?为什么日志没有存到文件里?我也遇到过这样的问题,当时也没有搞清楚问题的原因。今天,在读UNIX高级环境编程的fork一节时,意外的收获了对于该问题最终的解释,一切的原因都是因为I/O标准库的缓冲机制,下面就来详细介绍一下,I/O标准库的缓冲机制。


1.细节


在Linux系统下,write函数是无缓冲的,I/O标准库是带缓冲的,并且,I/O在不同的情境下,缓冲方式也是不同。首先通过一个例子说明一下,I/O标准库的缓冲机制。


#include "apue.h"
#if defined(MACOS)
#define _IO_UNBUFFERED  __SNBF
#define _IO_LINE_BUF  __SLBF
#define _IO_file_flags  _flags
#define BUFFERSZ(fp)  (fp)->_bf._size
#else
#define BUFFERSZ(fp)  ((fp)->_IO_buf_end - (fp)->_IO_buf_base)
#endif
void  pr_stdio(const char *, FILE *);
int
main(void)
{
  FILE  *fp;
  fputs("enter any character\n", stdout);
  if (getchar() == EOF)
    err_sys("getchar error");
  fputs("one line to standard error\n", stderr);
  pr_stdio("stdin",  stdin);
  pr_stdio("stdout", stdout);
  pr_stdio("stderr", stderr);
  if ((fp = fopen("/etc/passwd", "r")) == NULL)
    err_sys("fopen error");
  if (getc(fp) == EOF)
    err_sys("getc error");
  pr_stdio("/etc/passwd", fp);
  exit(0);
}
void
pr_stdio(const char *name, FILE *fp)
{
  printf("stream = %s, ", name);
  /*
   * The following is nonportable.
   */
  if (fp->_IO_file_flags & _IO_UNBUFFERED)
    printf("unbuffered");
  else if (fp->_IO_file_flags & _IO_LINE_BUF)
    printf("line buffered");
  else /* if neither of above */
    printf("fully buffered");
  printf(", buffer size = %d\n", BUFFERSZ(fp));
}


==*注意,==*在打印缓冲区状态之前,先对每个流执行I/O操作,第一个I/O操作通常造成为该流分配缓冲。结构成员_IO_file_flags、_IO_buf_base、_IO_buf_end和常量_IO_UNBUFFERED、_IO_LINE_BUFFERD是Linux中GNU标准I/O库定。应当了解,其他UNIX系统可能会有不同的标准I/O库实现。 如果运行两次上面的示例程序,一次使三个标准流与终端相连接,另一次使它们重定向到普通文件,则所得结果是:


$./buf              stdin、stdout、stderr都连至终端
  enter any character
  one line to standard error
  stream = stdin, line buffered, buffer size = 1024
  stream = stdout, line buffered, buffer size = 1024
  stream = stderr, unbuffered, buffer size = 1
  stream = /etc/passwd, fully buffered, buffer size = 4096
  $./buf < ./buf.c > std.out 2> std.err     三个流都重定向,再次运行该程序
  enter any character
  stream = stdin, fully buffered, buffer size = 4096
  stream = stdout, fully buffered, buffer size = 4096
  stream = stderr, unbuffered, buffer size = 1
  stream = /etc/passwd, fully buffered, buffer size = 4096


从中可以见,该系统默认的情况是:


  1. 当标准输入、输出连至终端时,它们时行缓冲的。航缓冲区长度是1024字节。(行缓冲区的输出条件是:缓冲区满或者遇到换行符,这就是我们为什么在printf的末尾需要添加'\n'的原因了)。注意,这并没有将输出入、输出的长度限制为1024字节,这只是缓冲区的长度。如果要将2048字节的行写到标准输出,则要进行两次write系统调用


  1. 当将这两个流重定向到普通文件时,它们就会变成是全缓冲的,其缓冲区长度是该文件系统优先选用的I/O长度(从stat结构中得到的st_blksize,也就是文件系统块的大小)。


  1. 标准出错在任何情况下都是无缓冲的,而对于普通文件按照系统默认是全缓冲的。


  1. 对于全缓冲而言,缓冲区清空或者说冲刷的时机是缓冲区满或者手动调用I/O标准库的fflush函数,该函数会将标准I/O库所使用的缓冲区的内容全部输出,但是并不代表这些内容已经写到文件系统中了,为了确保内容全部保存到物理文件中,需要再次调用sync或者fsync函数。


  1. 文章开头说write系统调用是无缓冲的,为什么这么设计呢?其实,这是Unix系统的设计精髓”机制与策略分离原则“,Unix提供了write这种与文件系统或者设备通信的机制,而标准I/O库就是基于该机制实现的带缓冲的策略。


  1. 通过上面的分析文章开头所讲的那些奇怪的问题相信大家都应该理解是什么原因了,所以看似简单的问题,其实背后隐藏了太多太多的学问了,以后不能轻视任何一个看似简单的"小问题”了,以小见大。


相关文章
|
27天前
|
存储 Linux C语言
Linux|如何安装和运行多个 glibc 库
Linux|如何安装和运行多个 glibc 库
54 5
|
4天前
|
缓存 监控 关系型数据库
深入理解Linux操作系统的内存管理机制
【7月更文挑战第11天】在数字时代的浪潮中,Linux操作系统凭借其强大的功能和灵活性,成为了服务器、云计算以及嵌入式系统等领域的首选平台。内存管理作为操作系统的核心组成部分,对于系统的性能和稳定性有着至关重要的影响。本文将深入探讨Linux内存管理的基本原理、关键技术以及性能优化策略,旨在为读者提供一个全面而深入的理解视角,帮助开发者和系统管理员更好地优化和管理Linux系统。
|
10天前
|
小程序 Linux
【编程小实验】利用Linux fork()与文件I/O:父进程与子进程协同实现高效cp命令(前半文件与后半文件并行复制)
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
|
10天前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
|
17天前
|
存储 缓存 Linux
Linux VFS机制详解
Linux VFS机制详解
19 1
|
19天前
|
Linux 编译器 C语言
Linux中的pkg-config:简化库依赖管理的利器
**pkg-config**是Linux下管理库依赖的工具,它通过读取库的`.pc`文件提供编译和链接参数。使用`pkg-config --cflags --libs &lt;library&gt;`获取编译和链接选项,例如`gcc -o test test.c $(pkg-config --cflags --libs glib-2.0)`。能进行版本检查、参数提取、依赖管理和路径搜索。列出所有包用`pkg-config --list-all`。最佳实践包括确保库正确安装、检查版本、配置`PKG_CONFIG_PATH`及使用构建工具。
|
23天前
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
17 1
|
3天前
|
Linux 编译器 vr&ar
【Linux】静态库和动态库
本文详细介绍了Linux系统中静态库和动态库的概念、区别、制作与使用方法,包括它们在链接时的区别、加载机制以及优缺点。
3 0
|
28天前
|
存储 编解码 Ubuntu
【QT】linux下alsa库的移植和QT中音视频的处理&笔记
【QT】linux下alsa库的移植和QT中音视频的处理&笔记
|
26天前
|
Linux
Linux异步io机制 io_uring
Linux异步io机制 io_uring
20 1