1.C标准库的I/O缓冲区 
        UNIX的传统 是Everything is a file,键盘、显示器、串口、磁盘等设备在/dev 目录下都有一个特殊的设备文件与之对应,这些设备文件也可以像普通文件(保存在磁盘上的文件)一样打开、读、写和关闭,使用的函数接口是相同的。用户程序调用C标准I/O库函数读写普通文件或设备,而这些库函数要通过系统调用把读写请求传给内核 ,最终由内核驱动磁盘或设备完成I/O操作。C标准库为每个打开的文件分配一个I/O缓冲区以加速读写操作,通过文件的FILE 结构体可以找到这个缓冲区,用户调用读写函数大多数时候都在I/O缓冲区中读写,只有少数时候需要把读写请求传给内核。以fgetc / fputc 为例,当用户程序第一次调用fgetc 读一个字节时,fgetc 函数可能通过系统调用 进入内核读1K字节到I/O缓冲区中,然后返回I/O缓冲区中的第一个字节给用户,把读写位置指 向I/O缓冲区中的第二个字符,以后用户再调fgetc ,就直接从I/O缓冲区中读取,而不需要进内核 了,当用户把这1K字节都读完之后,再次调用fgetc 时,fgetc 函数会再次进入内核读1K字节 到I/O缓冲区中。在这个场景中用户程序、C标准库和内核之间的关系就像在“Memory Hierarchy”中 CPU、Cache和内存之间的关系一样,C标准库之所以会从内核预读一些数据放 在I/O缓冲区中,是希望用户程序随后要用到这些数据,C标准库的I/O缓冲区也在用户空间,直接 从用户空间读取数据比进内核读数据要快得多。另一方面,用户程序调用fputc 通常只是写到I/O缓 冲区中,这样fputc 函数可以很快地返回,如果I/O缓冲区写满了,fputc 就通过系统调用把I/O缓冲 区中的数据传给内核,内核最终把数据写回磁盘或设备。有时候用户程序希望把I/O缓冲区中的数据立刻 传给内核,让内核写回设备或磁盘,这称为Flush操作,对应的库函数是fflush,fclose函数在关闭文件 之前也会做Flush操作

我们知道main 函数被启动代码这样调用:exit(main(argc, argv));。

main 函数return时启动代码会 调用exit ,exit 函数首先关闭所有尚未关闭的FILE *指针(关闭之前要做Flush操作),然后通 过_exit 系统调用进入内核退出当前进程.

C标准库的I/O缓冲区有三种类型:全缓冲、行缓冲和无缓冲。当用户程序调用库函数做写操作时, 不同类型的缓冲区具有不同特性。

全缓冲

如果缓冲区写满了就写回内核。常规文件通常是全缓冲的。 

行缓冲

如果用户程序写的数据中有换行符就把这一行写回内核,或者如果缓冲区写满了就写回内 核。标准输入和标准输出对应终端设备时通常是行缓冲的。

无缓冲

用户程序每次调库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的,这样用户程序产生的错误信息可以尽快输出到设备。

        除了写满缓冲区、写入换行符之外,行缓冲还有两种情况会自动做Flush操作。如果: 
用户程序调用库函数从无缓冲的文件中读取 
或者从行缓冲的文件中读取,并且这次读操作会引发系统调用从内核读取数据

       如果用户程序不想完全依赖于自动的Flush操作,可以调fflush函数手动做Flush操作。 
#include <stdio.h> 
int fflush(FILE *stream); 
返回值:成功返回0,出错返回EOF并设置errno
fflush函数用于确保数据写回了内核,以免进程异常终止时丢失数据,如fflush(stdout); 作为一个特例,调 用fflush(NULL)可以对所有打开文件的I/O缓冲区做Flush操作。

2. 用户程序的缓冲区
        在函数栈上分配的如char buf[10];之类的缓冲区, strcpy(buf, str);  str 所指向的字符串有可能超过10个字符而导致写越界,这种写越界可能当时不出错, 而在函数返回时出现段错误,原因是写越界覆盖了保存在栈帧上的返回地址, 函数返回时跳转到非法地址,因而出错。像buf 这种由调用者分配并传给函数读或写的一段内存通 常称为缓冲区(Buffer),缓冲区写越界的错误称为缓冲区溢出(Buffer Overflow)。如果只是出 现段错误那还不算严重,更严重的是缓冲区溢出Bug经常被恶意用户利用,使函数返回时跳转到一 个事先设好的地址,执行事先设好的指令,如果设计得巧妙甚至可以启动一个Shell,然后随心所欲 执行任何命令,可想而知,如果一个用root 权限执行的程序存在这样的Bug,被攻陷了,后果将很 严重。

       下图以fgets / fputs 示意了I/O缓冲区的作用,使用fgets / fputs 函数时在用户程序中也需要分配缓冲 区(图中的buf1 和buf2 ),注意区分用户程序的缓冲区和C标准库的I/O缓冲区。



3.内核缓冲区
(1)终端缓冲

终端设备有输入和输出队列缓冲区,如下图所示

以输入队列为例,从键盘输入的字符经线路规程过滤后进入输入队列,用户程序以先进先出的顺序 从队列中读取字符,一般情况下,当输入队列满的时候再输入字符会丢失,同时系统会响铃警报。 终端可以配置成回显(Echo)模式,在这种模式下,输入队列中的每个字符既送给用户程序也送给 输出队列,因此我们在命令行键入字符时,该字符不仅可以被程序读取,我们也可以同时在屏幕上 看到该字符的回显。
        注意上述情况是用户进程(shell进程也是)调用read/write等unbuffer I/O函数的情况,当调用printf/scanf(底层实现也是read/write)等C标准I/O库函数时,当用户程序调用scanf读取键盘输入时,开始输入的字符都存到C标准库的I/O缓冲区,直到我们遇到换行符(标准输入和标准输出都是行缓冲的)时,系统调用read将缓冲区的内容读到内核的终端输入队列;当调用printf打印一个字符串时,如果语句中带换行符,则立刻将放在I/O缓冲区的字符串调用write写到内核的输出队列,打印到屏幕上,如果printf语句没带换行符,则由上面的讨论可知,程序退出时会做fflush操作.
   
 
(2)虽然write 系统调用位于C标准库I/O缓冲区的底 层,被称为Unbuffered I/O函数,但在write 的底层也可以分配一个内核I/O缓冲区,所以write 也不一定是直接写到文件的,也 可能写到内核I/O缓冲区中,可以使用fsync函数同步至磁盘文件,至于究竟写到了文件中还是内核缓冲区中对于进程来说是没有差别 的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数据从进程B也能读到,因为内核空间是进程共享的, 而c标准库的I/O缓冲区则不具有这一特性,因为进程的用户空间是完全独立的.

(3)为了减少读盘次数,内核缓存了目录的树状结构,称为dentry(directory entry(目录下项) cache

(4)FIFO和UNIX Domain Socket这两种IPC机制都是利用文件系统中的特殊文件来标识的。FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道,各进程可以打开这个文件进行read / write ,实际上是在读写内核通道(根本原因在于这个file 结构体所指向的read 、write 函数 和常规文件不一样),这样就实现了进程间通信。UNIX Domain Socket和FIFO的原理类似,也需 要一个特殊的socket文件来标识内核中的通道,文件类型s表示socket,这些文件在磁盘上也没有数据块。UNIX Domain Socket是目前最广泛使用 的IPC机制.如下图:

4.stack overflow 无穷递归或者定义的极大数组都可能导致操作系统为程序预留的栈空间耗尽 程序崩溃(段错误)

UNIX环境高级编程——标准I/O库缓冲区和内核缓冲区的区别的更多相关文章

  1. UNIX环境高级编程---标准I/O库

    前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压 ...

  2. UNIX环境高级编程——标准I/O库

    对一个进程预定义了三个流,并且这三个流可以自动的被进程使用,它们是:标准输入.标准输出.和标准错误. 标准I/O库提供缓冲的目的是尽可能减少使用read和write的次数. 标准I/O库提供了三种类型 ...

  3. UNIX环境高级编程 标准IO库

    标准I/O库处理很多细节,使得便于用户使用. 流和 FILE 对象 对于标准I/O库,操作是围绕 流(stream)进行的.当用标准I/O打开或创建一个文件时,我们已使一个流与一个文件相关联. 对于A ...

  4. UNIX环境高级编程——标准I/O库函数和Unbuffered I/O函数

    以写文件为例,C标准I/O库函数(printf(3) .putchar(3) .fputs(3) )与系统调用write(2) 的关 系如下图所示. 库函数与系统调用的层次关系 open .read ...

  5. UNIX环境高级编程——标准IO-实现查看所有用户

    #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h&g ...

  6. (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  7. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  8. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  9. (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. 安装Leanote极客范的云笔记

    前言 在这个互联网知识呈爆炸增长的时代,作为一个程序员要掌握的知识越来越多,然再好的记性也不如烂笔头,有了笔记我们就是可以时常扒拉扒拉以前的知识,顺便可以整理下自己的知识体系. 如今市面上云笔记产品, ...

  2. android基础-界面开发注意事项

    做安卓开发时一定要注意,主线程不能更改UI界面,如果出现程序运行时崩溃的情况,如果没有明显的语法错误,请检查自己的进程是否出现冲突,崩溃.如果有与后台的连接,即请求向服务器发送请求的时尤其需要注意,或 ...

  3. spring web项目下,判断项目是否启动完成

    本文同时发布于见鬼网:https://faceghost.com/article/483341 概述:spring 加载完成后,我们有时候会做一些初始化工作,如加载一些缓存,DB,等,这里采用实现Se ...

  4. 在OpenCV3.1.0中使用SIFT,SURF算法

    写在前边: 1.我使用的是python2.7 + OpenCV3.1.0 2.OpenCV3.0.0+的文档有很大问题,很多文档写的还是OpenCV2.0+, OpenCV3.0+根本用不了,其中有一 ...

  5. sublime安装配置

    http://www.sublimetext.com.cn/ 打华东师范大学校赛的时候,学长谈论到这个编辑器.自定义背景多行多光标同时编辑酷炫爆了.感觉这是一个万能的文本编辑器.通过配置可以写多种语言 ...

  6. MySQL DATEDIFF() 函数

    定义和用法 DATEDIFF() 函数返回两个日期之间的天数. 语法 DATEDIFF(date1,date2) date1 和 date2 参数是合法的日期或日期/时间表达式. 注释:只有值的日期部 ...

  7. MySQL之sql文件的导入导出

    window下 1.导出整个数据库(无需登录mysql)mysqldump -u 用户名 -p 数据库名 > 导出的文件名mysqldump -u dbuser -p dbname > d ...

  8. UE4使用UMG接口操作界面

    原文链接:http://gad.qq.com/article/detail/7181131 本文首发腾讯GAD开发者平台,未经允许,不得转载 UE4的蓝图之强大让人欲罢不能,但是实际在项目的开发中,C ...

  9. Android图表库MPAndroidChart(一)——了解他的本质,方能得心应手

    Android图表库MPAndroidChart(一)--了解他的本质,方能得心应手 我们项目中经常会遇到一些统计图,比如折线图,线形图等,在一些运动健康类的App中尤其的常见,这画起来要命,我以前就 ...

  10. XMPP 测试工具

    XMPP 测试工具(金庆的专栏 2016.10)XMPP的客户端Pidgin有个Debug Window, 可以显示所有发送和接收的xml节.但是无法发送任意的自定义节.<XMPP高级编程-使用 ...