转载:https://blog.csdn.net/kyang_823/article/details/79496561

一、文件I/O和标准I/O
文件I/O:文件I/O也称为不带缓冲的I/O(unbuffered I/O)。不带缓冲指的是每个read,write都调用内核中的一个系统调用。也就是一般所说的低级磁盘I/O,遵循POSIX相关标准,任何兼容POSIX标准的操作系统上都支持文件I/O。——是操作系统提供的基本IO服务,与OS绑定,特定于Linux或Unix平台。

标准I/O:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,不依赖系统内核,所以移植性强。又称为高级磁盘I/O,遵循ANSI C相关标准。只要开发环境中有标准I/O库,标准I/O就可以使用。(Linux 中使用的是glibc,它是标准C库的超集。不仅包含ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux 下既可以使用标准I/O,也可以使用文件I/O)。标准I/O库处理很多细节,例如缓冲分配,以优化长度执行I/O等。

文件I/O:读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销。使用时一般用户需要自己维护一个用户缓冲区,不过在Linux系统中,都使用内核高速缓冲用于提高效率,因此文件I/O的读写系统调用实际上是在内核缓冲区和进程的用户缓冲区之间进行的数据复制。

标准I/O:可以看成在文件I/O的基础上由标准I/O库封装并维护了缓冲机制(流缓冲,都在用户空间)。比如调用fopen函数,不仅打开一个文件,而且建立了一个流缓冲(读写模式下将建立两个缓冲区),还创建了一个包含文件和缓冲区相关信息的FILE结构,从而先读写流缓冲区,必要时再访问实际文件,从而减少了系统调用的次数,使用库函数在用户空间的流缓冲上和用户交互的效率高于直接从内核读写数据的效率,因此提高了I/O效率。

带缓存的I/O操作是C标准库实现的,第一次调用带缓存的文件操作函数时标准库会自动分配内存(通常是调用malloc在用户空间上分配堆内存)作为流缓存并且读出一段固定大小的内容存储在缓存中。所以以后每次的读写操作并不是针对磁盘上的文件直接进行的,而是针对标准库的流缓存的。何时从磁盘中读取文件或者向磁盘中写入文件有标准库的机制控制。

文件I/O:所有I/O函数都是围绕文件描述符进行的。当打开一个文件时,即返回一个文件描述符,后续的I/O操作也都使用该文件描述符进行操作。可以访问不同类型的文件如普通文件、设备文件和管道文件等。

标准I/O:所有操作都是围绕流(stream)进行的。当用标准I/O库打开或创建一个文件时,即将一个流和一个文件相关联。通常只用来访问普通文件(???)。

不带缓冲I/O:指进程不提供缓冲(但内核还是提供缓冲的),每调用一次write或read函数,直接进行系统调用。系统内核对磁盘的读写都会提供一个块缓冲(也被称为内核高速缓冲),用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲,当块缓冲满时才会数据写入磁盘。

如果要写数据到文件上(就是写入磁盘),内核先将数据写入到内核中所设的缓冲区,假如这个缓冲储存器的长度是100个字节,调用系统调用 ssize_t write (int fd,const void * buf,size_t count); 写操作时,设每次写入长度count=10个字节,那么需要调用10次系统调用才能把内核缓冲区写满,之前数据还是在缓冲区,并没有写入到磁盘,缓冲区满时才进行实际上的IO操作,即数据写入到磁盘。因此“不带缓冲”不是没有缓冲而是没有直接写进磁盘。

带缓冲I/O:指进程提供一个流缓冲,当用fwrite函数往磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件(比如流缓冲区满,或主动刷新流缓冲)时才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。(即双重缓冲)

上面的例子,内核缓冲区长度100字节,调用不带缓冲的IO函数write()需要调用10次系统调用,这样系统效率低。而使用标准I/O函数时,用户层建立另一个缓冲区(流缓冲),假设流缓冲的长度是50字节,用标准C库函数fwrite()将数据写入这个流缓冲区,流缓冲区满50字节后再一次调用系统调用write()将数据写入内核缓冲内,如果内核缓冲也被填满,那么内核缓冲区内数据就被写入到文件(实质是磁盘)。由此可以看出: ① 标准IO操作最终还是要调用无缓冲IO系统调用(带缓冲I/O本身就是在不带缓冲I/O基础上提供缓冲实现的),它们并不直接读写磁盘 ; ② 增加用户/流缓冲区可以减少系统调用的次数。

正常情况下,和磁盘交互的读写文件的大致流程:

当应用程序尝试读取某块数据的时候, ① 如果这块数据已经存放在页缓存(也就是上面提到的内核高速缓存)中,那么这块数据就可以立即返回给应用程序,而不需要经过实际的物理读盘操作。 ② 如果数据在应用程序读取之前并未被存放在页缓存中,那么就需要先将数据从磁盘读到页缓存中去。

对于写操作来说,应用程序也会将数据先写到页缓存中去(如果是调用标准库I/O进行写操作,那么首先是写到标准库的流缓冲区,在一定条件之后,再写到页缓冲内;如果是系统调用,那么直接写到页缓冲内),数据是否被立即写到磁盘上取决于应用程序所采用的写操作机制:

如果用户采用同步写机制,那么数据会立即从页缓存写到磁盘上,应用程序会一直等到数据被写完为止;
如果用户采用延迟写机制,那么应用程序就完全不需要等到数据全部被写到磁盘,数据只要写到页缓存中就可以了。在延迟写机制的情况下,操作系统会定期地将放在页缓存中的数据刷到磁盘上。
如果用户采用异步写机制。在数据完全写到磁盘上的时候会通知应用程序。与异步写机制不同,延迟写机制在数据完全写到磁盘上的时候不通知应用程序,因此延迟写机制本身就存在数据丢失的风险,而异步写机制则不会有这方面的担心。
无缓存IO操作的数据流向:数据——内核缓存区——磁盘
标准IO操作的数据流向:数据——流缓存区——内核缓存区——磁盘

标准I/O中,一般由系统选择缓存的长度,并自动分配。标准I/O库在关闭流的时候自动释放缓存。

在标准I / O库中,一个效率不高的不足之处是需要复制的数据量。 当使用每次一行函数fgets和fputs时,通常需要复制两次数据:一次是在内核高速缓存和标准I/O缓存之间(当调用read和write时),第二次是在标准I/O缓存(通常系统分配和管理)和用户程序中的缓存(fgets的参数就需要一个用户行缓存指针)之间。

程序在读写文件时既可以调用C标准I/O库函数,也可以直接调用底层POSIX标准的的Unbuffered I/O函数,那么用哪一组函数好呢?

用Unbuffered I/O函数每次读写都要进内核,调一个系统调用比调一个用户空间的函数要慢很多,所以使用时在用户空间开辟I/O缓冲区还是必要的,此时用C标准I/O库函数就比较方便,并且省去了自己管理I/O缓冲区的麻烦。
用C标准I/O库函数要时刻注意I/O缓冲区的存在会使得和实际文件有可能不一致,在必要时需调用fflush() 。
我们知道Unix的传统是Everything is a file,I/O函数不仅用于读写常规文件,也用于读写设备,比如终端或网络设备。在读写设备时通常是不希望有缓冲的,例如向代表网络设备的文件写数据就是希望数据通过网络设备发送出去,而不希望只写到缓冲区里就算完事儿了,当网络设备接收到数据时应用程序也希望第一时间被通知到,所以设备的编程通常直接调用Unbuffered I/O函数。
fflush将流所有未写的数据送入(刷新)到内核(内核缓冲区),fsync将所有内核缓冲区的数据写到文件(磁盘)。至于究竟写到了文件中还是内核缓冲区中对于进程来说是没有差别的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数据从进程B也能读到,因为内核空间是进程共享的。而C标准库的I/O缓冲区则不具有这一特性,因为进程的用户空间是完全独立的。

文件I/O和标准I/O的更多相关文章

  1. UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

    lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于 ...

  2. 打开的文件符合PDF/A标准,且已在只读模式下打开,以防被修改

    PDF/A是一种用于长期归档和保留电子文档的ISO标准.您扫描到PDF的文档符合PDF/A的规范.您可以指定是否要用本查看模式查看文档. PDF/A 问题提示:“打开的文件符合PDF/A标准,且已在只 ...

  3. 比较两个文件的异同Python3 标准库difflib 实现

    比较两个文件的异同Python3 标准库difflib 实现 对于要比较两个文件特别是配置文件的差异,这种需求很常见,如果用眼睛看,真是眼睛疼. 可以使用linux命令行工具diff a_file b ...

  4. Linux cat命令详解(连接文件并打印到标准输出设备上)

    cat:连接文件并打印到标准输出设备上 一.命令格式: cat [-AbeEnstTuv] [--help] [--version] filename 二.参数说明: -n 或 --number:由 ...

  5. 文件IO函数和标准IO库的区别

    摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...

  6. Linux 文件操作——系统调用和标准I/O库

    一.什么是文件 在讲述文件操作之前,我们首先要知道什么是文件.看到这个问题你可能会感觉到可笑,因为对于用过计算机的人来说,文件是最简单不过的概念了,例如一个文本是一个文件,一个work文档是一个文件等 ...

  7. Linux目录文件详解FHS标准(2013.09.05)

    Linux 目录配置的依据FHS(Filesystem Hierarchy Standard)标准,将目录分成为四种交互作用的形态: 四种形态的具体解释: 可分享的:可以分享给其他系统挂载使用的目录, ...

  8. 第 13 章 文件输入/输出 (标准I/O)

    /*-------------------------- count.c -- 使用标准 I/O --------------------------*/ #include <stdio.h&g ...

  9. C语言文件I/O和标准I/O函数

    读取/写入  相对于文件而言 输入/输出 相对于程序/内存而言 一切皆文件,键盘.显示屏也是文件,只不过是特殊的标准文件: 标准文件:标准输入.标准输出.标准错误:---->对应的文件指针:st ...

随机推荐

  1. Matlab plotyy画双纵坐标图实例

    Matlab plotyy画双纵坐标图实例 x = 0:0.01:20;y1 = 200*exp(-0.05*x).*sin(x);y2 = 0.8*exp(-0.5*x).*sin(10*x);[A ...

  2. zabbix3.0.4 部署之八 (zabbix3.0.4 报警前端配置)

    (如何让报警信息推送----微信.邮件)(邮件与微信一样就不在重复) 创建一个用户 将用户加入administrator组 添加之前设置的报警媒介脚本 设置报警等级 创建动作 配置报警内容 设置报警条 ...

  3. C++ 类、对象、class

    一.对象初始化 1.不能在类声明中对数据成员初始化,因为类只是一个抽象类型,不占存储空间,无处容纳数据. 2.若某类的数据成员都是public,则可以像结构体一样初始化,如 Time t={12,21 ...

  4. 部分cocoscreator左右移动代码

    cc.Class({extends: cc.Component, properties: { // 主角跳跃高度 jumpHeight: 0, // 主角跳跃持续时间 jumpDuration: 0, ...

  5. ANALYZE - 收集与数据库有关的统计

    SYNOPSIS ANALYZE [ VERBOSE ] [ table [ (column [, ...] ) ] ] DESCRIPTION 描述 ANALYZE 收集有关 PostgreSQL ...

  6. VS2017 ATL创建ActiveX编程要点

    VS2017 ATL创建ActiveX控件编程要点: 一.创建vs项目需要安装器visual studio installer中: 安装 visual studio扩展开发中的 用于x86和x64的V ...

  7. 进程的互斥运行:CreateMutex函数实现只运行一个程序实例

    HANDLE hMutex=CreateMutex(NULL,TRUE,"HDZBUkeyDoctorTool"); if(hMutex) { if(ERROR_ALREADY_E ...

  8. vue的[__ob__: Observer]

    为什么会获取不到里面的值 因为:vue data 里面值都是有这个属性的.这是被vue接管的数据,observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭o ...

  9. 去掉PhpStorm打开自动启动的项目

    有时候启动的项目很大,导致很卡,而且在编辑器中更改也没效果,简单暴力的办法 找到项目的目录,删除其中的.idea文件夹

  10. python Matplotlib 系列教程(三)——绘制直方图和条形图

    在本章节我们将学习如何绘制条形图和直方图 条形图与直方图的区别:首先,条形图是用条形的长度表示各类别频数的多少,其宽度(表示类别)则是固定的: 直方图是用面积表示各组频数的多少,矩形的高度表示每一组的 ...