两个缓冲空间:kernel buffer和io buffer

先看一张图,稍后将围绕这张图展开描述。图中的fd table、open file table以及两个inode table都可以不用理解,只需要知道它们体现出来的文件描述符和磁盘文件之间的对应关系:文件描述符fd(例如图中的fd=3)是对应磁盘上文件的。

在Linux下,我们经常会在IO操作时不可避免的涉及到文件描述符,因为Linux下的所有IO操作都是通过文件描述符来完成的。但是,文件描述符是一个非常底层的概念,通过它操作的数据,都是二进制数据,所以通过文件描述符完成IO的模式通常也称为裸IO(Raw IO)。而且,直接通过底层的文件描述符进行编程会比较麻烦,因为是二进制数据,它缺少很多功能,比如无法指定编码,无法指定换行符(换行符有多种:\n、\n\r、\r)等等。注意fd是用户空间的,它仅仅是一个数值而已,并不是想象中感觉比较底层就在内核空间。

所以,现代高级语言(比如C、Python、Java、Golang)都提供了比文件描述符更高一层次的标准IO库,比如C的标准IO库是stdio,Python的标准IO库是IO模块,等等。使用这些标准IO库中的函数进行IO操作时,都会使用比文件描述符更高一层次的对象,例如C中称为IO流(io stream),其它面向对象的语言中一般称为IO对象,为了方便说明,这里统称为IO对象。上图中的F就是文件对象。

标准IO库可以看作是文件描述符的更高层次的封装,提供了比文件描述符操作IO更多的功能。例如,可以在IO对象上指定编码、指定换行符,此外还在用户空间提供了一个标准IO库的缓冲空间,通常可称为stdio bufferIO buffer,而这些功能在文件描述符上都是没有的。另外,标准IO库既然是高层封装,当然也会提供用户不使用这些功能(比如不使用IO Buffer),而是直接使用文件描述符,那么这时候的文件对象就相当于是文件描述符了,这时候的IO操作模式也就是裸IO模式。

所有从硬件读取或写入到硬件的数据,默认都会经过操作系统维护的这个Kernel Buffer。正如上图中描述的是读数据过程。

例如,cat进程想要读取a.log文件,cat进程是用户空间进程,它自身没有权限打开文件以及读文件数据,它只能通过系统调用的方式陷入内核,请求操作系统帮助读取数据,操作系统读取数据后会将数据放入到page cache(刚才已说明,对于普通文件维护的Kernel buffer称为page cache或buffer cache)。然后还要将内核空间page cache中的数据拷贝到用户空间的IO Buffer缓冲空间(因为cat程序的源代码中使用了标准IO库stdio),然后cat进程从自己的IO Buffer中读取数据。这就是整个读数据的过程。

需要注意的是,虽然这两段缓冲空间都在内存中,但仍然有拷贝操作,因为内核的内存空间和用户进程的虚拟内存空间是隔离的,用户空间进程没有权限访问到内核空间的内存,但是内核具有最高权限,允许访问任何内存地址。换句话说,在将Kernel Buffer的数据拷贝到IO Buffer空间的过程中,需要陷入到内核,OS需要掌控CPU。

此外,Linux也提供了所谓的直接IO模式,只需使用一个称为O_DIRECT的标记即可,这时会绕过Kernel Buffer,直接将硬件数据拷贝到用户空间。虽然看上去直接IO少了一个层次的参与感觉性能会更优秀,但实际上并非如此,操作系统为内核缓冲空间做了非常多的优化,使得并不会因此而降低性能。最典型且常见的一个优化是预读功能,它表示在读数据时,会比所请求要读取的数据量多读一点放入到Kernel Buffer,这样在下次读取接下来的一段数据时可以直接从Kernel Buffer中取数据,而无需再和硬件IO交互。所以,使用直接IO模式的场景是非常少的,一般只会在自带了完整缓冲模型的大型软件(比如数据库系统)上可能会使用直接IO模式。

上面所描述的都是读操作,关于写操作,这里不再多花篇幅去描述,整体过程和读是类似的,都会经过IO Buffer和Kernel Buffer,只是其中一些细节有所不同,如果感兴趣,可以阅读《Linux/Unix系统编程手册》的第13章。

每天3分钟操作系统修炼秘籍(13):两个缓冲空间Kernel Buffer和IO Buffer的更多相关文章

  1. 每天3分钟操作系统修炼秘籍(14):IO操作和DMA、RDMA

    点我查看秘籍连载 I/O操作和DMA.RDMA 用户进程想要执行IO操作时(例如想要读磁盘数据.向磁盘写数据.读键盘的输入等等),由于用户进程工作在用户模式下,它没有执行这些操作的权限,只能通过发起对 ...

  2. 每天3分钟操作系统修炼秘籍(12):OOM和swap分区

    点我查看秘籍连载 OOM和swap分区 进程的虚拟内存空间是映射到整个物理内存空间的,所以在进程自身看来它拥有了整个物理内存,它也能使用整个物理内存,只需在使用的时候请求操作系统帮忙分配更多空间即可. ...

  3. 每天3分钟操作系统修炼秘籍(6):Idle进程

    点我查看秘籍连载 CPU的归属:Idle进程 操作系统并不总是繁忙.例如个人PC上任务比较轻,多数时候都无法充分利用CPU,导致CPU处于空闲状态.但CPU既然通电了,它就得运行,那么在它没有任务需要 ...

  4. Vim修炼秘籍之语法篇

    前言 少年,我看你骨骼精奇,是万中无一的武学奇才,维护世界和平就靠你了,我这有本秘籍<Vim修炼秘籍>,见与你有缘,就十块卖给你了! 如果你是一名 Vimer,那么恭喜你,你的 Vim 技 ...

  5. MySQL Innodb的两种表空间方式

    要说表空间,MySQL的表空间管理远远说不上完善.换句话说,事实上MySQL根本没有真正意义上的表空间管理.MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间.只 ...

  6. 在编写wpf界面时候中出现如下错误: 类型引用不明确。至少有两个名称空间(“System.Windows”和“System.Windows”)中已出现名为“VisualStateManager”的类型。请考虑调整程序集 XmlnsDefinition 特性。

    wpf中类型引用不明确.至少有两个名称空间(“System.Windows”和“System.Windows”)中已出现名为“VisualState 你是不是用了WPFToolKit?如果是的,那原因 ...

  7. java io系列13之 BufferedOutputStream(缓冲输出流)的认知、源码和示例

    本章内容包括3个部分:BufferedOutputStream介绍,BufferedOutputStream源码,以及BufferedOutputStream使用示例. 转载请注明出处:http:// ...

  8. 9.11排序与查找(一)——给定两个排序后的数组A和B,当中A的末端有足够的缓冲空间容纳B。将B合并入A并排序

    /**  * 功能:给定两个排序后的数组A和B,当中A的末端有足够的缓冲空间容纳B.将B合并入A并排序. */ /** * 问题:假设将元素插入数组A的前端,就必须将原有的元素向后移动,以腾出空间. ...

  9. 操作系统开发系列—13.g.操作系统的系统调用 ●

    在我们的操作系统中,已经存在的3个进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是 ...

随机推荐

  1. MySQL 5.7安装(多实例)最佳实践

    MySQL 5.7安装(多实例)最佳实践,接上一篇<MySQL 5.7安装最佳实践>在已有的实例上面新增一个实例,主要是为了资源合理利用:下面是具体的步骤: 1.新实例目录规划,my.cn ...

  2. JS里面的铠甲合体!

    本标题党又回来了,最近在专心研究一些JS基础性的书籍,以期把原理都了解透彻,所以写文章的频率就降了下来.但是今天我必须要来写一下子,为什么呢,因为今天周五!先说明一下JS里面的拆箱与装箱指的是JS封箱 ...

  3. Redis 的底层数据结构(整数集合)

    当一个集合中只包含整数,并且元素的个数不是很多的话,redis 会用整数集合作为底层存储,它的一个优点就是可以节省很多内存,虽然字典结构的效率很高,但是它的实现结构相对复杂并且会分配较多的内存空间. ...

  4. CSS中的各种单位

    单位 描述                                                                                               ...

  5. 【Redis】Could not get a resource from the pool 实乃集群配置问题

    先说些题外话~自上次确诊为鼻窦炎+过敏性鼻炎到现在已经一个月了,最初那会,从下午到晚上头疼难忍.大概是积劳成疾,以前流鼻涕.打喷嚏的时候从来没有注意过,结果病根一下爆发. 关键在于锁定问题,开始治疗一 ...

  6. 实践开发:vue框架重点知识分析

    一个VUE项目的主树: assets文件夹是放静态资源: components是放组件: router是定义路由相关的配置; view视图: app.vue是一个应用主组件: main.js是入口文件 ...

  7. C#发送电子邮件(SMTP)及outlook.com账号之概要

    这是关于c#发送电子邮件(SMTP)的技术笔记,以”简报“形式呈现. 因为最后成功通过outlook.com发送了邮件,所以,我觉得还是有必要 记录一下其中的要点. 一.技术核心 .net Frame ...

  8. Java描述设计模式(16):代理模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 在电商高速发展的今天,快递的数量十分庞大,甚至出现了快递代理行业,简单的说就是快递的主人没有时间收快递,会指定一个 ...

  9. 06 python学习笔记-常用模块(六)

    一.  模块.包 1.什么是模块? Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句,是用来组织代码的.模块能定义函数 ...

  10. Mutex vs Semaphore vs Monitor vs SemaphoreSlim

    C#开发者(面试者)都会遇到Mutex,Semaphore,Monitor,SemaphoreSlim这四个与锁相关的C#类型,本文期望以最简洁明了的方式阐述四种对象的区别. 线程安全 教条式理解 如 ...