文件系统和设备管理

110 阅读28分钟

文件系统是操作系统中负责管理持久数据的子系统,负责把用户的文件存到磁盘硬件中,即使计算机断电,磁盘的数据并不会丢失,所以可以持久化的保存文件

Linux文件系统为每个文件分配两个数据结构,索引节点和目录项

主要用来记录文件的元信息和目录层次结构

  • 索引节点,用来记录文件的元信息,包括inode编号,文件大小,访问权限,创建时间,修改时间,数据在磁盘的位置等。索引节点是文件的唯一标识,他们之间一一对应,都会被存储在硬盘中,所以索引节点同样占用磁盘空间
  • 目录项,用来记录文件的名字,索引节点指针以及与其他项目项的层级关联关系。多个目录项关联起来就会形成目录结构,目录项是由内核维护的一个数据结构,不存放于磁盘,而是缓存在内存

由于索引节点唯一表示一个文件,而目录项记录着文件的名字,所哟目录项和索引节点的关系是多对一,也就是说,一个文件可以有多个别名

目录也是文件,也用索引节点唯一标识

目录是文件,持久化存储在磁盘,目录项是内核一个数据结构,缓存在内存。因为 如果查询目录频繁从磁盘读,效率很低,所以内核会把已经读过的目录用目录项这个数据结构缓存在内存中,大大提高了文件系统的效率

目录项不仅仅可以表示目录,也可以表示文件

磁盘的一个扇区只有512B,一次只读一个扇区效率就会很低

文件系统把磁盘的多个扇区组成一个逻辑块,每次读写的最小单位就是逻辑块(数据块)Linux中的逻辑块大小为4KB,一次读写八个扇区,大大提高了磁盘的读写效率

Image.webp

索引节点存储在硬盘,为了加速访问,通常会把索引节点加载到内存中

磁盘进行格式化的时候,会被分层三个存储区域,分别是超级块、索引节点区和数据块区

超级块:当文件系统挂载时进入内存

索引节点区:当文件被访问时进入内存

虚拟文件系统

操作系统为了对用户提供一个统一的接口,在用户层与文件系统层引入了虚拟文件系统(VFS)

VFS定义了一组所有文件系统都支持的数据结构和标准接口,程序员只需要了解VFS提供的统一接口

对于Linux有

Image.webp

Linux中,根据存储位置不同,有以下三类文件系统:

  • 磁盘的文件系统

  • 内存的文件系统

  • 网络的文件系统

文件系统

从用户角度来看,读取文件的过程

  • open系统调用打开文件,fd=open(name,flag)
  • write写数据,write(fd,...)
  • 使用完成后,要用close系统调用关闭文件,避免资源的泄露

打开了一个文件后,操作系统会为每个进程维护一个打开文件表,文件表里的每一项 文件描述符,文件描述符是打开文件的标识

操作系统在打开文件表中维护着打开文件的状态和信息

  • 文件指针,对打开文件的某个进程来说是唯一的

  • 文件打开计数器,文件关闭时,操作系统必须重用其打开文件表条目,否则内存空间不够用,

  • 文件磁盘位置,保存在内存中,加快读取速度

  • 访问权限,每个进程打开文件都要有一个访问模式,保存在进程的打开文件表中,以便操作系统能允许或拒绝之后的IO请求                                                                                                                                  

用户和操作系统对文件的读写操作是有差异的,用户习惯以字节的方式读写文件,而操作系统则是以数据块来读写文件,那屏蔽掉这种差异的工作就是文件系统了。

文件系统的基本操作单位是数据块

文件的存储

文件的数据存储在磁盘上,分为来连续空间存放方式和非连续空间存放方式

非连续空间存放方式又分为链表方式和索引方式

  • 连续空间存放方式
    • 文件存放在磁盘连续的物理空间中,数据紧密相连,读写效率很高,因为一次磁盘寻道就可以读出整个文件
    • 在存放前必须先知道文件的大小,文件系统才能找到一块大小合适的连续空间
    • 所以文件头需要指定起始块的位置和长度
    • 存在磁盘空间碎片和文件长度不易扩展的缺陷
  • 非连续空间存放方式
    • 链表式,可以连续分配和文件动态扩展,但不支持直接访问(FAT除外)
    • 链表方式的存放是离散的,不是连续的,可以消除磁盘碎片,提高磁盘空间的利用率,使文件的长度可扩展。又可分为隐式链表和显式链接
    • 隐式链表,文件头包括第一块和最后一块的位置,每个数据块里流出一个指针空间,用于存放下一个数据块的地址,缺点是无法直接访问数据块,只能通过指针顺序访问文件,以及数据块指针消耗了一定的存储空间,稳定性较差,若链表中的指针丢失,则文件数据就会丢失
    • 显式链接,用于链接文件各数据块的指针,显式地存放在内存的一张链接表中,该表在整个磁盘仅设置一张, 每个表项中存放链接指针,指向下一个数据块号
    • 查找记录在内存中进行,显著提高检索速度,大大减少访问磁盘的次数,缺点是不适用于大磁盘,因为大磁盘的链接表的大小会很大,占用过多内存
    • 索引式,为每个文件创建一个索引数据块,存放指向文件数据块的指针列表,类似于目录,文件头需要包含指向索引数据块的指针,通过文件头知道索引数据块的位置,再通过索引数据块里的索引信息找到对应的数据块
    • 创建文件时,索引块的所有指针都设为空,首次写入第i块时,先从空闲空间中取得一个块,再将其地址写到索引块的第i个条目
    • 索引的优点
      • 文件的创建、增大、缩小很方便
      • 没有磁盘碎片的问题
      • 支持顺序读写和随机读写
    • 索引数据存放在磁盘块,假设文件很小,仍然需要额外分配一块空间来存放索引数据,所以会带来的开销
    • 如果文件很大,大到一个索引数据块放不下索引信息,就要通过组合的方式来处理大文件的存储
  • 链表+索引
    • 链式索引块,在索引数据块留出一个存放下一个索引数据块的指针,相当于在索引数据块的索引信息用完后,以指针的方式再找到下一个索引数据块
  • 索引+索引
    • 多级索引块,通过一个索引块来存放多个索引数据块

早期Unix文件存放方式

多级索引,对于大文件的访问,需要大量查询,效率较低

空闲空间管理

空闲表法

为所有空闲空间建立一张表,表内容包括空闲区的一个块号和该空闲区的块个数,这个方式是连续分配的

当请求分配磁盘空间时,系统依次扫描空闲表里的内容,直到找到一个合适的空闲区域为止。当用户撤销一个文件时,系统回收文件空间。这时,也需顺序扫描空闲表,寻找一个空闲表条目并将释放空间的第一个物理块号及它占用的块数填到这个条目中

仅有少量空闲区时才有较好效果,因为如果有大量空闲区,空闲表就会很大,查询效率就会很低,这种技术适用于建立连续文件

空闲链表法

每一个空闲块有一个指针指向下一个空闲块,所以只需要在主存中保存一个指针指向第一个空闲块,简单,但是不能随机访问,工作效率低,消耗存储空间

位图法

利用二进制的一位来表示磁盘中一个磁盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应

当值为0,表示对应盘块空闲,1表示对应盘块已分配

位图法不仅用于数据空闲块的管理,还用于inode空闲块的管理,因为inode也是存储在磁盘的

文件系统的结构

Linux用位图的方式管理空闲空间,用户在创建一个新文件时,Linux内核会通过inode的位图找到可用的inode并进行分配,要存储数据时,会通过块的位图找到空闲的块,并分配,这样能表示的最大空间才128M,还不够

所以Linux文件系统将这样一个结构(一个块的位图+一系列的块+一个块的inode的位图+一系列的inode结构)称为块组,如果有N个块组,就能表示N大的文件

现在的文件系统由大量块组组成

第一个块是引导块,在系统启动时用于启用引导,之后跟着一个个连续的块组

  • 超级块,包含文件系统的重要信息,包括inode总数,块总数,每个块的inode个数,每个组块的块个数等
  • 块组描述符,包含各个组块的状态,比如块组中空闲块和inode的数目等,每个块组都包含了文件系统中所有块组的组描述信息符
  • 数据位图和inode位图
  • inode列表,包含块组中所有的inode,inode用于保存文件系统中与各个文件和目录相关的所有元数据
  • 数据块

为什么每个块里有很多重复的信息,比如超级块和块组描述符,都是全局信息?

  • 如果系统崩溃破坏了超级块或者块组描述信息,有关文件系统结构和内容的所有信息都会丢失,如果有冗余的副本,就有可能恢复该信息

  • 使文件和管理数据尽可能接近,减少了磁头寻道和旋转,提高了文件系统的性能

目录的存储

普通文件的块里保存的是文件数据,目录文件的块保存的是目录里面一项一项的文件信息

存储方式:

  • 列表
    • 简单的,一项一项地记录下文件信息(文件名,文件inode,文件类型等)
    • 按照inode就能找到对应文件,如果一个目录有较多文件,则效率较低
  • 哈希表
    • 对文件名进行哈希计算,把哈希值保存起来,就能通过查找哈希表的形式找到文件

    • 插入删除简单,但需要预备措施来避免哈希冲突

    • 目录查询是在磁盘上反复搜索,不断进行IO操作,开销较大,所以可以将当前使用的文件目录缓存在内存降低磁盘操作的次数,提高文件系统的访问速度

硬链接和软链接

实现给某个文件取别名的操作,这两者都是特殊的文件

硬链接是多个目录项中的索引节点指向一个文件,也就是指向同一个inode。

因为inode是不可能跨越文件系统的,每个文件系统都有各自的inode和列表,所以硬链接也是不可用于夸文件系统的。由于多个目录项都是指向一个inode,所以只有删除文件的所有硬链接以及源文件时,系统才能彻底删除该文件

软链接相当于重新创建一个文件,这个文件有独立的inode,这个文件的内容是另外一个文件的路径,所以访问软链接相当于访问了另外一个文件,所以软链接是跨文件系统的,甚至目标文件被删除了,链接文件还是存在的,只是指向的文件找不到了而已

缓冲与非缓冲IO

根据是否利用标准库缓冲,可以把文件IO分为缓冲IO和非缓冲IO

  • 缓冲IO利用标准库的缓存实现文件的加速访问,而标准库再通过系统调用访问文件
  • 非缓冲IO,直接通过系统调用访问文件,不经过标准库缓存

比如说,程序遇到换行时才真正输出,而换行前的内容,就是被标准库暂时缓存了起来,减少了系统调用的次数

直接与非直接IO

Linux为了减少磁盘IO次数,在系统后,会把用户数据拷贝到内核中缓存起来,这个内核缓存空间就是页缓存,只有当缓存满足某些条件时,才发起磁盘IO请求

根据是否利用操作系统的缓存,可以分为直接IO和非直接IO

  • 直接IO,不会发生内核缓冲和用户程序之间数据复制,而是直接经过文件系统访问磁盘
  • 非直接IO,读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户拷贝给内核缓存,再由内核决定何时写入数据到磁盘

如果使用文件操作类的系统调用函数时指定了0_DIRECT标志,使用直接IO,否则默认使用非直接IO

以下几种场景会触发内核缓存的数据写入磁盘:

  • 在调用write的最后,发现内核缓存数据过多,内核会把数据写到磁盘上

  • 用户主动调用sync,内核缓存会刷到磁盘上

  • 当内存十分紧张,无法再分配页面时,也会把内核缓存的数据刷到磁盘上

  • 内核缓存的数据的缓存时间超过某个时间时,也会刷到磁盘上

阻塞IO与非阻塞IO 异步与同步IO

同步IO分为

  • 阻塞IO
  • 非阻塞IO
  • 基于非阻塞IO的多路复用

异步IO分为

  • 异步IO

IO是分为

1、数据准备过程

2、数据从内核空间拷贝到用户进程缓冲区的过程

阻塞IO会阻塞在1和2,而非阻塞IO和基于阻塞IO的多路复用只会阻塞在过程2,这三都是同步IO

异步IO则在这俩过程都不会阻塞

进程写文件时,进程发生了崩溃,已写入的数据会丢失吗?

不会的,因为进程在执行write(使用缓冲IO)系统调用的时候,实际上是将文件数据写到了内核的page cache,它是文件系统中用于缓存文件数据的缓冲,所以即使进程崩溃了,文件数据还是保留在内核的page cache,读数据时,也是从page cache读取,因此还是依然读的进程崩溃前写入的数据

内核会找个合适的时机,将page cache里的文件数据持久化到磁盘

但是如果page cache里的文件数据,在持久化到磁盘化到磁盘之前,系统发生了崩溃,这部分数据就丢了

在程序里调用fsync函数,在写文件的时候,立即将文件数据持久化到磁盘,可以解决系统崩溃导致的文件数据丢失

page cache

本质是由Linux内核管理的内存区域。通过mmap和buffered IO将文件读取到内存空间实际上都是读取到page cache中

page是内存管理分配的基本单位,page cache由多个page构成,page在操作系统中大小通常为4KB,page cache的大小为4KB的整数倍

不是所有page都被组织为page cache

Linux系统中供用户访问的内存分为两个类型,即:

  • File_backer pages:文件备份页也就是page cache中的page,对应磁盘上的若干数据块
  • Anonymous pages匿名页对不上磁盘的任何磁盘块,它们是进程运行的内存空间

为什么Linux不把page cache成为block cache,这不是更好吗?

因为从磁盘中加载到内存的数据不仅仅放在page cache中,还放在buffer cache中。

内存是一种珍贵的资源,当内存不够用时,内存管理单元需要提供调度算法用来回收相关内存空间

内存空间回收的方式通常是swap,即交换到持久化存储设备上。

File-backed pages(page cache)的内存回收代价较低。它通常对应于一个文件上的若干顺序块,因此可以通过顺序IO的方式移入磁盘。如果page cache上没有进行写操作(没有脏页),甚至不会将page cache回盘,因为数据完全可以通过再次读取磁盘文件得到

page cache的难点在于脏页回盘

Anonymouspages的回收代价较高,因为它通常随机地写入持久化交换设备,另一方面无论是否有写操作,无论是否发生写操作,为了确保数据不丢失,Anonymous pages在swap时必须持久化到磁盘

Swap与缺页中断

Swap机制指的是当物理内存不够用,内存管理单元需要提供调度算法来回收相关内存空间,然后将清理出来的内存空间给当前内存申请方

swap机制的存在是因为Linux系统提供了虚拟内存管理机制,每一个进程认为其独占内存空间,因此所有进程的内存空间之和远远大于物理内存,所有进程的内存空间之和超过物理内存的部分就要交换到磁盘上

操作系统以page为单位管理内存,当操作系统发现要访问的数据不在内存时,操作系统可能会将数据以页的方式加载到内存中。上述过程被称为缺页中断,当操作系统发生缺页中断时,就会通过系统调用将page再次读取到内存中

主内存的空间是有限的,当主内存不包含可以使用的空间时,操作系统会从选择合适的物理内存页驱逐回磁盘,为新的内存页让出位置,选择待驱逐页的过程叫做页面替换,替换操作又会触发swap机制

如果物理内存够大,可能不需要swap机制,但是swap机制在这种情况下仍有优势,对于有概率发生内存泄漏的进程,swap交换分区很重要,可以确保内存泄漏不至于导致物理内存不够用,最终导致系统崩溃。但是内存泄漏会频繁触发swap,此时非常影响操作系统的性能

Linux通过一个swappiness参数来控制swap机制,参数值可设为0-100,控制系统swap的优先级

  • 高数值,频繁swap,进程不活跃时主动将其换出物理内存

  • 低数值,低频swap,可确保交互式不因为内存空间频繁交换到磁盘导致相应延迟提高

为什么swapcache也是page cache的一部分?

因为匿名页以及Active先被交换到磁盘上后,再加载回内存中,由于读入到内存后原来的swap file还在,所以swapcached也可以认为是file backed page 即属于page cache

执行free命令,有两列名为 buffers和cached也有一列名为-/+buffers/cache

其中cached列表示当前的页缓存,buffers列表示当前块缓存占用量。

即pagecache用于缓存文件的页数据,buffer cache用于缓存块设备(如磁盘)的块数据

page cache与buffer cache 的共同目的都是加速数据IO

  • 写数据时首先写到缓存,将写入的页标记为dirty,然后向外部存储flush也就是缓存写机制中的写回(write-back)(Linux默认情况下不采用写穿法write-through)

  • 读数据时首先读取缓存,如果未命中再去外部存储读取,并且将读取的数据加入缓存,操作系统总是积极地将所有空闲内存都用作pagecache和buffer cache,如果内存不够用也会用LRU等算法淘汰部分缓存页

Linux2.4版本的内核之前,page cache和buffer cache完全分离,导致很多数据被缓存两次,浪费内存

2.4之后,两块缓存近似融合在一起,如果一个文件的页加载到了page cache,同时buffer cache只需维护块指向页的指针就可以了

所以现在提起page cache,大都同时指page cache和buffer cache两者,统称为page cache

操作系统基于page cache的读缓存机制提供预读机制,

写穿法以牺牲系统IO吞吐量作为代价,确保一旦写入,数据就不会丢失

写回法会在系统崩溃的情况下无法确保数据已经写入磁盘,可能存在数据丢失的问题

page cache的优势

  • 加快数据访问,如果数据已经在内存中进行缓存,下次访问就不需要通过磁盘IO,直接命中内存缓存即刻
  • 减少IO次数,提高系统磁盘IO吞吐量,通过一次IO将多个page装入page cache能够减少磁盘IO次数,提高系统磁盘IO吞吐量

page cache的劣势

  • 需要占用额外的物理内存空间,内存在紧俏时会导致频繁的swap,导致系统的磁盘IO负载上升

  • 对于应用层并没有提供很好的管理API,几乎是透明管理。导致很难优化page cache 的使用策略。因此一些应用选择在用户空间实现page管理

  • 在某些应用场景下比direct IO多一次磁盘读IO和磁盘写IO

缓存文件IO:用户空间要读写一个文件并不直接与磁盘交互,中间夹了一层缓存pagecache

直接文件IO:用户空间读取的文件直接与磁盘交互,没有中间page cache

直接IO的特点

写:如果返回成功,数据就真的落盘了

读:每次读操作是真的从磁盘中读取,不会从文件系统的缓存中读取

为了屏蔽多个输入输出设备之间的差异,每一个设备都有一个叫设备控制器的组件,硬盘控制器和视频控制器等

控制器都很清楚的知道对应设备的用法和功能,CPU是通过设备控制器来控制设备

  • 写入这些寄存器,操作系统可以命令设备发送数据,接收数据,执行某些其他操作

  • 通过读取这些寄存器,操作系统可以了解设备的状态,是否准备好接收一个新的命令

控制器中有三类寄存器:状态寄存器,命令寄存器,数据寄存器

  • 数据寄存器,CPU向IO设备写入需要传输的数据

  • 命令寄存器,CPU发送一个命令,告诉IO设备,要进行输入输出操作,于是交给IO设备执行,完成后会把状态寄存器的状态标记为完成

  • 状态寄存器,目的是告诉CPU现在正在工作或工作已经完成,如果正在工作状态,CPU再发送数据或命令过来,都是没有用的,直到当前工作完成,CPU才能发送下一个字符和命令给该设备

输入输出设备可分为两大类:块设备和字符设备

  • 块设备,把数据存储在固定大小的块中,每个块有自己的地址,硬盘、USB是常见的块设备

  • 字符设备,以字符为单位发送或接收一个字符流,字符设备是不可寻址的,也没有任何寻道操作,比如鼠标

块设备通常传输的数据量会很大,所以控制器设立了一个可读写的数据缓冲区

  • CPU写入数据到控制器的缓冲区时,当缓冲区的数据囤够了一部分,才会发给设备
  • CPU从控制器的缓冲区读取数据时,也需要缓冲区囤够一部分,才会拷贝到内存

目的是为了减少对设备的频繁操作

CPU是如何与设备的控制寄存器和数据缓冲区进行通信的?

  • 端口IO,每个控制寄存器被分配一个IO端口,可以通过特殊的汇编指令操作这些寄存器,比如in/out类似的指令

  • 内存映射IO,将所有控制寄存器映射到内存空间当中,可以像写内存一样读写数据缓冲区

IO控制方式

每个设备都有一个设备控制器,控制器相当于一个小CPU,它可以自己处理一些事情。

当CPU给设备发送了一个指令,让设备控制器去读取设备的数据,它读完的时候,要怎么通知CPU呢?

控制器的寄存器一般有状态标记位,用来表示输入或输出操作是否完成。于是有轮询等待的方案,让CPU一直查询寄存器的状态,直到状态标记为完成。很傻瓜的方式,会占用CPU的全部时间

中断,通知操作系统数据已经准备好了,一般有一个硬件的中断控制器,当设备完成任务后触发中断到中断控制器,中断控制器就通知CPU,一个中断产生时,CPU就需要停下当前处理的事来处理中断

中断分为两种,软中断用代码调用触发如INT

硬件中断,通过硬件通过中断控制器

但是中断对于频繁读写数据的磁盘不友好,CPU经常会被打断,会占用CPU大量的时间,这里有DMA功能来解决,Direct Memory Access功能,它使得设备在CPU不参与的情况下,能够自行完成把设备IO数据存放到内存中,实现MDA控制器需要有DMA硬件的支持  

DMA工作方式:

  • CPU对DMA控制器下发指令,告知想读取多少数据,读取的数据的存放位置
  • DMA控制器会向磁盘控制器发出指令,通知它从磁盘读数据到其内部的缓冲区,接着磁盘控制器将缓冲区的数据传输到内存
  • 当磁盘控制器把数据传输到内存的操作完成后,磁盘控制器在总线上发出一个确认成功的信号到DMA控制器
  • DMA控制器收到信号后,发送中断通知CPU指令已完成,CPU就能直接使用内存里现成的数据了

也就是说,CPU要读取磁盘数据时,只要给DMA控制器发送指令,就返回去做其他事情,当磁盘数据拷贝到内存后,DMA控制器通过中断的方式告诉CPU数据已经准备好了。仅仅在传送开始和结束时需要CPU干预

设备驱动程序

设备控制器屏蔽了设备的众多细节,但控制器的存储器、缓冲区的使用模式都是不同的,为了屏蔽设备控制器的差异,引入了设备驱动程序

设备控制器是硬件,设备驱动程序是操作系统的一部分。设备驱动程序发出操控设备控制器的指令后,才能操作设备控制器

不同的设备控制器功能不同,但设备驱动程序会提供同一的接口给操作系统。这样不同的设备驱动程序,就可以以相同的方式介入操作系统

通用块层

对于块设备,为了减少不同的块设备的差异带来的影响,Linux通过一个同一的通用块层来管理不同的块设备

通用块层是处于文件系统和磁盘驱动中间的一个块设备抽象层

  • 第一个功能:向上为文件系统和应用程序,提供访问块设备的标准接口,向下把各种不同的磁盘设备抽象为同一的块设备,并在内核层面提供一个框架来管理这些块设备的驱动程序

  • 第二个功能:给文件系统和应用程序发来的IO请求排队,紧接着会对队列重新排序、请求合并等方式,也就是IO调度,主要目的是为了提高磁盘读写的效率

Linux内存有5种IO调度

  • 没有调度算法
    • 不对文件系统和应用程序的IO做任何处理,通常用于虚拟机IO,此时磁盘IO调度算法由物理机系统负责
  • 先入先出调度算法
    • 简单的先进入IO调度队列的IO请求先发生
  • 完全公平调度算法
    • 大部分系统的默认IO调度器,它为每个进程维护了一个IO调度队列,并按照时间片来均匀分配每个进程的IO请求
  • 优先级调度
    • 优先级高的请求先发生,适用于大量进程的系统,比如桌面环境、多媒体应用等
  • 最终期限调度算法
    • 分别为读写请求创建不同的IO队列,这样可以提高机械磁盘的吞吐量,确保达到最终期限的请求被优先处理,适用于在IO压力较大的场景,比如数据库等

存储系统IO软件分层

Image.webp

  • 文件系统层,包括虚拟文件系统和其他文件系统的具体实现,它向上为应用程序提供了标准的文件访问接口,向下通过通用块层来存储和管理磁盘数据
  • 通用块层,包括块设备的IO队列和IO调度器,它会对文件系统的IO请求进行排队,再通过IO调度器,选择一个IO发给下一层的设备
  • 设备层,包括硬件设备、设备控制器和驱动程序,负责最终物理设备的IO操作

有饿了文件系统接口之后,可以通过文件系统的命令行操作设备,也可以通过应用程序,调用read、write函数,像读写文件一样操作设备

除了读写操作,还需要有检查特定设备的功能和属性。需要ioctl接口,它表示输入输出控制接口,用于配置和修改特定设备属性的通用接口。

由于存储系统的IO是很慢的,Linux有不少缓存机制来提高IO的效率

  • 为了提高文件访问的效率,有页缓存、索引节点缓存、目录项缓存等多种缓存机制,目的是为了减少对块设备的直接调用。

  • 为了提高块设备的访问效率,会使用缓冲区,来缓存块设备的数据

键盘敲入A时,发生了什么?

当用户输入了字符,键盘控制器会产生扫描码数据,并将其缓冲在键盘控制器的寄存器中,紧接着键盘控制器通过总线给CPU发送中断请求

键盘的中断处理程序是在键盘驱动程序初始化时注册的,,那键盘中断处理函数的功能就是从键盘控制器的机器的缓冲区扫描码,再根据扫描码找到用户所在的键盘输入的的字符,就会吧扫描码翻译成对应显示字符的ASCII码

得到了ASCII码后,就会把其放到读缓冲区队列,接下来就是要把显示字符显示屏幕了,显示设备的驱动程序会定时从读缓冲区队列读取数据放在写缓冲区,最后把写缓冲区队列的数据一个一个写入到现实设备的控制器的寄存器中的数据缓冲区,最后将这些数据显示在屏幕里。显示出结果后,恢复被中断进程的上下文