unix I/O缓存

标签: unix io
14人阅读 评论(2) 收藏 举报

原文:https://lizijie.github.io/


四个月前碰到一个问题
为了提高程序IO效率,会对输出流进行缓冲管理,不会立即写到磁盘。但我在帖子里提到了两个疑问:
1. tail延时5-10秒,才读取到日志
2. 部分log并不是按逻辑顺序输出。比如

LOG("1");
LOGERR("2");
// 在日志文件里,2竟先打印
2
1

最近从《UNIX环境高级编程》第五章-标准IO/库读到了相关内容,认为可以解答以上2个疑问。
以下大部分内容摘自抄原文。

标准I/O提供了以下3种类型的缓冲:
1. 全缓冲。在这种情况下,填满标准I/O缓存后才进行实际I/O操作。对于驻留在磁盘上的文件通常是由标准I/O实施全缓冲的。
  缓存区可由标准I/O例程自动地冲洗(例如,当填满一个缓存区时),或者可以调用函数fflush冲洗一个流。

2. 行缓冲。在这种情况下,当输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个字符(用标准I/O函数fputc),但只有在写了一行之后才进行实际I/O操作。当流涉及一个终端时(如标准输入和标准输出),通常使用行缓冲。

3. 不带缓冲。标准I/O库不对字符进行缓冲存储。
例如,若用标准I/O函数fputs写15个字符到不带缓冲的流中,我们就期望这15个字符能立即输出。
  标准错误流stderr通常是不带缓冲的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个换行符。

ISO C 要求下列缓冲特征
1. 当且仅当标准输入和输出并不指向交互式设备时,它们才是全缓冲的。
2. 标准错误决不会是全缓冲的。

根据以上说明,我们来分析问题
1. 程序将日志输出到文本,使用系统默认的缓冲区,只有当填满缓存区时才会将文本写到磁盘文本。缓冲区越大,tail越晚显示新增的内容。
以下例子通过stat获取系统为每个文本定义的适当缓冲区长度。

include <sys/stat.h>
int stat(const char ×restrict pathname, strut stat *restrict buf);

// 读取stat.st_blksize I/O缓冲大小
struct stat st;
stat(path, &st);
bklsize_t size = st.st_blksize;

listDirblkSize遍历我机器某目录下所有文件,I/O缓存区大小都是4096,足可以存储2048个中文。导致5-10秒后才写磁盘也是有可能的。
具体关于stat.st_blksize的详细说明请读者自己查阅。

$ lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch
Distributor ID: Fedora
Description:    Fedora release 27 (Twenty Seven)
Release:    27
Codename:   TwentySeven


$ sh listDirIoblkSize.sh 
file:./1185632884.jpg block size:4096 byte
file:./1915660170.jpg block size:4096 byte
file:./alien-8.95 block size:4096 byte
file:./alien_8.95.tar.xz block size:4096 byte
file:./centos6.9.tar block size:4096 byte
file:./code-1.22.2-1523551168.el7.x86_64.rpm block size:4096 byte
file:./Evernote_6.8.7.6387.exe block size:4096 byte
file:./Evernote.sublime-settings block size:4096 byte
file:./[FHD-1080P]MIRD-114 block size:4096 byte
  1. 我是通过nohup proc_name >> log 2>&1 &启动程序的,自定义的LOGERR,其内部实现是输出到不带缓冲的stderr,所以比stdout先写到磁盘,导致出现日志不按逻辑顺序输出。

可以使用以下两个函数中的一个,更改系统默认能分配的缓存区。

#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
void stvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

  使用stebuf函数打开或关闭缓冲机制。为了缓冲进行I/O,参数buf必须指向一个长度为BUFSIZE的缓冲区。通常此之后该流就是全缓冲的,但是如果该流与一个终端设备相关,那麽某些系统也可以将其设置为行缓冲的。为了关闭缓冲,将buf设置为NULL。
  使用setvbuf,我们可以精确地说明所需的缓冲类型。这是用mode参数实现的:
  _IOFBF  全缓冲
  _IOLBF  行缓冲
  _IONBF  不带缓冲
  如果指定一个不带缓冲的流,则忽略buf和size参数。
  如果指定全缓冲或行缓冲,则buf和size可选择地指定一个缓冲区及其长度。
  如果该流是带缓冲的,而buf是NULL,则标准I/O库将自动地为该流分配适当长度的缓冲区。

查看评论

Unix下的I/O模型

一、Unix下共有五种I/O模型 1、阻塞I/O 2、非阻塞I/O 3、I/O复用(select和poll) 4、信号驱动I/O(SIGIO) 5、异步I/O(Posix.1的aio_系列函数) ...
  • liu88010988
  • liu88010988
  • 2016-03-03 14:45:19
  • 534

《unix网络编程》(12)五种I/O模型

《unix网络编程》(11)tcp服务器的几种常见状况分析的“服务器进程终止”提到客户阻塞于fgets所以没办法收到服务器发的FIN,只有当客户再次输入文本并发送给服务器后才会从套接字中读取,这时才知...
  • u013074465
  • u013074465
  • 2015-04-04 21:09:48
  • 2196

UNIX网络编程----非阻塞式I/O(十六)

UNIX网络编程-----非阻塞式I/O 一、概述 套接字的默认状态是阻塞的。这就意味着放发出一个不能立即完成的套接字调用时,其进程被投入睡眠,等待相应操作完成,可能阻塞的套接字调用可分为以下四类: ...
  • yusiguyuan
  • yusiguyuan
  • 2013-09-13 13:55:54
  • 1502

unix文件和i/o流

1. 关于unix文件结构 在unix/linux文件系统中,一切皆是文件,目录是文件,设备是文件,文件是文件......文件需要有文件的各项属性,在unix中,可以使用stat函数族来获取文件属性...
  • zhuzhiwen2015
  • zhuzhiwen2015
  • 2016-05-15 16:27:58
  • 676

图解UNIX的I/O模型

一、简述UNIX系统将所有的外部设备都看作一个文件来看待,所有打开的文件都通过文件描述符来引用。文件描述符是一个非负整数,它指向内核中的一个结构体。当打开一个现有文件或创建一个新文件时,内核向进程返回...
  • lihao21
  • lihao21
  • 2016-06-09 13:29:12
  • 2096

《UNIX环境高级编程》笔记:第3章 文件I/O

“本章说明了UNIX系统提供的基本I/O函数。因为read和write都在内核执行,所以称这些函数为不带缓冲的I/O函数。在只使用read和write情况下,我们观察了不同的I/O长度对读文件所需时间...
  • fenghou1st
  • fenghou1st
  • 2015-02-01 15:03:50
  • 295

《unix高级环境编程》高级 I/O——非阻塞 I/O

非阻塞I/O使我们可以调用 open、write和read 这样的I/O操作,并使这些操作不会永远阻塞。如果这种操作不能完成,则立即出错返回,表示该操作若继续执行将阻塞。 对于一个给定的文件描述符由以...
  • chenhanzhun
  • chenhanzhun
  • 2014-11-15 08:56:37
  • 952

本质论-Unix系统I/O

简单总结一下学习系统IO的内容,本文只涉及如何使用系统IO,不涉及内部的实现。 为什么需要I/O? I/O是解决如何在外部设备和内存之间交换数据的问题,也就是如何从外部设备...
  • jewes
  • jewes
  • 2013-03-02 15:26:35
  • 2288

《unix高级环境编程》终端 I/O——终端 IO 基本概述

本文是对终端IO的基本描述,终端主要是标识符的使用,只有在实践中才能够进一步理解。...
  • chenhanzhun
  • chenhanzhun
  • 2014-11-22 16:08:46
  • 1695
    个人资料
    等级:
    访问量: 7万+
    积分: 636
    排名: 8万+
    最新评论