[IO系统]01 IO子系统

来源:互联网 发布:阿里云200m 编辑:程序博客网 时间:2024/05/16 13:45

从整个IO调用链层面俯视整个链路,其穿越“千山万水”,最终会到胜利的彼岸——“设备层”


接下来,分别说明每个层次的主要功能,后续文章再详细分析和说明。

1.1   应用

顾名思义,应用就是程序,是用户态的。

用户在进行IO读写操作编程时,有两种方法:

1. 直接通过系统调用(如sys_open,sys_read,sys_write等等)操作文件,但是这种方法不够方便。

2. 通过glib库提供的函数进行文件操作,如open/fopen,read/fread, write/fwrite等,glib库的函数也是对系统调用的封装。

1.2   库函数

glibc是GNU实现的实现的一套标准C的库函数。

说道glibc,不的不提两种用户态的两类文件操作函数。

1. “缓冲”文件系统 – 用户态带缓存(与文件系统没有关系)

缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器而定。

fopen, fclose, fread,fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等

2.“非缓冲”文件系统 – 用户态不带缓存(与文件系统没有关系)

缓冲文件系统是借助文件结构体指针(FILE)来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据(openmode:r和ra)。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针(但是有文件描述符),只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它,只作简单介绍。

open, close, read,write, getc, getchar, putc, putchar 等。

1.3   系统调用

系统调用是操作系统为用户态进程与硬件设备进行交互提供了一组接口。

其作用是什么呢?1、把用户从底层的硬件编程中解放出来;2、极大的提高了系统的安全性;3、使用户程序具有可移植性。

用户如何通知系统进行这种调用呢?有一个叫做用户程序接口(applicationprogram interface, API)的专门就是用来给系统说明用什么功能的。和系统调用不同的是他仅仅在我们开来就是一个函数,而系统调用是通过软中断向内核发出一个明确的功能请求。(稍后的实验我们可以看到这一点)。

那么还有一个问题就是所谓的软中断的入口参数是什么呢,实际上就和函数的参数列表一样,除了知道他的中断号还要知道功能代码还有就是要知道他的输入是什么(见Linux内核分析一)这就是入口参数。那么系统调用 通过软中断或系统调用指令向内核发出一个明确的请求,内核将调用内核相关函数来实现(如sys_read() , sys_write() , sys_fork())。用户程序不能直接调用这些Sys_read,sys_write等函数。这些函数运行在内核态。

 

1.4   虚拟文件系统

文件系统是内核的功能,是一种工作在内核空间的软件,访问一个文件必须要需要文件系统的存在才可以。Linux 可以支持多达数十种不同的文件系统,它们的实现各不相同,因此 Linux 内核向用户空间提供了虚拟文件系统这个统一的接口用来对文件系统进行操作。<必要性>

虚拟文件系统是位于用户空间进程和内核空间中多种不同的底层文件系统的实现之间的一个抽象的接口层,它提供了常见的文件系统对象模型(如 i-node, file object, page cache, directory entry, etc.)和访问这些对象的方法(如 open, close, delete, write, read, create, fstat, etc.),并将它们统一输出,类似于库的作用。从而向用户进程隐藏了各种不同的文件系统的具体实现,这样上层软件只需要和 VFS 进行交互而不必关系底层的文件系统,简化了软件的开发,也使得 linux 可以支持多种不同的文件系统。

1.5   具体文件系统

具体文件系统按照不同的标准来区分,有很多种分法,在此处,主要介绍两个:

非日志型文件系统

在非日志型文件系统中,对文件系统实施一个写操作,内核会首先修改对应的元数据,然后修改数据块。如果在写入元数据时,文件系统发生崩溃或某种故障,那么数据的一致性将会遭到破坏。fsck 命令可以在下次重启时检查所有的元数据并修复数据一致性,但是如果文件系统非常大,或者系统运行关键业务不允许停机,使用非日志型文件系统的风险会非常高。

日志型文件系统

日志型文件系统的区别在于,在进行对文件系统写数据之前,写将数据写到「日志区」,然后再写入文件系统,在写入文件系统之后删除日志。日志区可以在文件系统内部也可以在文件系统外部。日志区的数据称作文件系统日志,这些数据包含了修改了的元数据,也可能包含将要修改的数据。写日志也会带来一定的额外开销。

1.6   Page Cache

Page cache,又称页高速缓存。page cache的大小为一页,通常为4K。

Page Cache中存放的数据是文件数据在内存中的副本,因此Page Cache 管理与内存管理系统和文件系统都相关:一方面Page cache 作为物理内存的一部分,需要参与物理内存的分配回收过程,另一方面Page cache中的数据来源于存储设备上的文件,需要通过文件系统与存储设备进行读写交互。从操作系统的角度考虑,Page cache 可以看做是内存管理系统与文件系统之间的联系纽带。因此,Page cache管理是操作系统的一个重要组成部分,它的性能直接影响着文件系统和内存管理系统的性能。

Page cache后续会专门介绍

1.7   通用块层

Generic Block layer处理所有和块设备相关的操作,最关键是数据结构是 bio 结构体。bio结构体是 file system layer 到 blocklayer 的接口。 当执行一个写操作时,文件系统层将数据写入 page cache(由 block buffer 组成),将连续的块放到一起,组成 bio 结构体,然后将 bio 送至 Generic Block layer。

Generic Block layer处理bio 请求,并将这些请求链接成一个队列,称作 IO 请求队列,这个连接的操作就称作 IO 调度.

1.8   IO调度层

IO调度器的总体目标是减少磁盘的寻道时间(因此调度器都是针对机械硬盘进行优化的),IO 调度器通过两种方式来减少磁盘寻道:合并和排序。

合并即当两个或多个 IO 请求的是相邻的磁盘扇区,那么就将这些请求合并为一个请求。通过合并请求,多个 IO 请求只需要向存储设备驱动层发送一个请求指令,减少了磁盘的开销。

排序就是将不能合并的 IO 请求,根据请求磁盘扇区的顺序,在请求队列中进行排序,使得磁头可以按照磁盘的旋转顺序的完成 IO 操作,可以减小磁盘的寻道次数。

对IO调度器的参数进行修改,可以改善IO系统的整体性能

Linux 2.4 只使用了一种通用的 IO 算法。到 Linux 2.6 实现了 4 种 IO 调度模型,其中 anticipatory 在 2.6.33 中被移除。

 

1.9   存储设备驱动层

IO调度器调用存储设备层的回调函数request_fn,request_fn函数对请求队列中每个bio进行分别处理,根据bio中的信息向磁盘控制器发送命令。处理完成后,调用完成函数end_bio以通知上层完成。

1.10     存储设备内部处理

存储芯片根据收到的驱动指令,对数据做出处理。

附更为详细的IO系统框图


未完,待续

0 0
原创粉丝点击