大话存储系列22——存储系统内部IO 中

4、卷管理层IO

卷管理层在某种程度上来讲是为了弥补底层存储系统的一些不足之处的,比如LUN空间的动态管理等。卷管理层最大的任务是做Block级的映射。对于IO的处理,卷层只做了一个将映射翻译之后的IO向下转发的动作以及反向过程。另外,应用程序可以直接对某个卷进行IO操作而不经过文件系统。我们所说的不经过文件系统,并不是说Bypass系统内核缓存的Direct IO,而是完全不需要FS处理任何块映射关系。这时就需要由应用程序自行管理底层存储空间,而且此时不能对这个卷进行FS格式或者其他未经应用程序运行的更改操作,一旦发生将导致数据被破坏。

卷管理层将底层磁盘空间虚拟化为灵活管理的一块块卷,然后又将卷同时抽象为两种操作系统设备:块设备和字符设备。比如在AIX系统下,/dev/lv 、/dev/fslv 、/dev/hdisk等字样表示块设备,而/dev/rlv、/dev/rfslv、/dev/rhdisk等带有r字样的设备一般就是字符设备。同一个物理设备会同时被抽象为字符和块两种逻辑设备,用户程序可以直接对块设备和字符设备进行IO操作。这两个设备也是用于上层程序直接对卷进行访问的唯一接口,有各自的驱动——块设备驱动和字符设备驱动。在IO路径的层层调用过程中,IO
Manager 访问卷的时候其实就是访问对应的设备驱动(这一点在系统IO模块架构图中并没有体现),向他们发起SystemCall的。

1、块设备:

    最近一段时间一直在做一个块设备模块的性能分析、优化工作,因此,对Linux文件系统层的代码进行了初略的阅读,颇有心得。Linux的文件系统的确非常庞大,层次也非常清晰。以前一直将注意力集中在了设备管理的层面,所以,通过这次的阅读分析,可以从用户程序的system call开始,将读写请求整个贯穿到底层硬件的DMA。下面对块设备与文件系统之间的接口关系进行阐述,主要解决“文件系统是如何访问块设备的?”这个问题。

    块设备可以在两种情况下发起probe(探测)过程,一是当总线驱动发现了块设备,回调该块设备驱动的probe函数,例如磁盘设备的驱动;二是驱动程序主动probe块设备,例如软RAID驱动程序,在insmod该程序后,会主动调用probe函数。Probe函数是设备驱动程序的核心函数,对于一类设备驱动而言,该函数的实现方式是雷同的,具有固定的格式。

    在块设备的probe过程中,首先需要实例化一个对象——gendisk,该对象在内核中对块设备进行了抽象。Gendisk对象初始化成功之后调用add_disk()函数将gendisk添加到系统中,在add_disk过程中需要生成bdev_inode结构,该结构维护了一个文件系统所需的inode节点,以及块设备的更高层次抽象bdev。该结构描述如下:

struct bdev_inode {

    struct block_device bdev;   //块设备 

    struct inode vfs_inode;     //文件系统inode节点

};

    如下图所示,在inode节点中一系列ops指针,指向具体的操作函数集。

如果inode描述的是一个块设备,那么ops函数集指向标准的块设备操作函数集。当用户open一个块设备时,会找到对应的inode,生成一个file对象,并且将inode中的file_operations(i_op)赋值给file对象中的f_op,从而建立了文件系统接口与块设备之间的接口。

如果inode描述的是一个文件系统,那么inode中的操作函数集将指向文件系统具体的操作函数集。从这个思路来看,inode是vfs层的接口,block device是一个特殊的文件系统,具体文件系统在注册时,需要将回调的方法注册到inode中。/fs/Block_dev.c文件就是block device这一特殊文件系统的具体实现,其中实现了与文件系统之间接口的各种函数,包括file_operations函数集。大家感兴趣不妨研究一下。

在UNIX类操作系统下,块设备表现为一个文件,而且应用程序可以向块设备发起任何长度的IO,就像对文件进行IO时一样,比如512B、1500B、5000B等、IO长度可以为任何字节,而不需要为磁盘扇区的整数倍,然而,块设备也是由底层物理设备抽象而来的,而底层物理设备所能接受的IO长度必须为扇区的整数倍,所以块设备具有一个比较小的缓存来专门处理这个映射转换关系。

块设备一般使用Memory Mapping的方式映射到内存地址空间,这段空间以Page(一般以4KB)为单位,所以访问块设备就需要牵涉到OS缺页处理(Page Fault)方式来读写数据。比如应用程序向某个块设备卷发起一个程度为1500B的IO读,卷管理层接收到这个IO之后将计算这个1500B的IO所占用的扇区总数以及所落入的Page地址,并且进入缺页处理流程从底层物理设备将这个Page对应的扇区读入,这里的IO请求为1500B,所以OS会从底层物理设备读取对应的1个Page大小的数据进入缓存,然后缓存中再将对应的1500B返回给应用程序。

应用程序对块设备发起读IO,块设备就得同时向底层物理设备发起对应的转换后的IO,不管应用程序向块设备发起多少长度的IO,块设备向底层物理设备所发起的IO长度是恒定的(一般为4K,即缓存Page大小)。所以块设备向底层设备发起的读IO属性永远是小块IO,而且对同一个线程发起的IO不会并发只能顺序,对多个线程共同发起的IO才会并行,也就是说每个线程在底层的IO都为顺序执行(限于读IO)。这一点是块设备非常致命的缺点,比如一个应用程序256KB的读IO操作,会被块设备切开成64个4KB的读IO操作,这无疑是非常浪费的,会更快的耗尽底层存储的标称IOPS。但对于写IO来讲,块设备底层会有一定的merge_request操作,既可以对写IO进行合并、覆盖、重排扥操作。

其实UNIX类系统下的设备与文件系统管理下的一个文件无异,唯一区别就是直接对块设备进行IO操作的话,无需执行文件——块映射查询而已。

读操作对于块设备来讲还不至于产生太过恶劣的性能影响,而写IO则会更加严重地摧残存储设备的性能。由于块设备向底层发起的所有IO均以缓存Page大小为单位,现代操作系统的Page一般为4KB大小,如果某应用程序需要写入0.5KB数据,或者4.5KB数据,那么块设备不能直接把对应长度的数据直接写入底层设备。其浪费可谓是惊人而且无法容忍的,我们来看一个例子。下列数据显示了AIX系统上使用IO测试工具对一个块设备进行4096B、2000B、2048B、5000B写IO时系统底层向物理磁盘的IO统计情况如下图:

程序发起的IO时候,不对齐4KB的IO Size会导致OS首先读入对应的Page数据,修改,然后再写入对应的Page数据,所以可以看到写动作伴随了一定程度的读动作,也就是写惩罚。


2、字符设备:

传统的字符设备本来是专指一类接收字符流的设备比如物理终端、键盘灯,这种设备的特点是可以直接对设备进行最底层的操作而不使用缓存(但是必须有Queue),而且每次IO都必须以一个字符为单位(卷所抽象出来的字符设备以一段连续扇区为一个单位)。所以具有这种特点的实际设备或者抽象设备都被称为字符设备。而将卷抽象为字符设备并不是说将IO从扇区改为字符,而只是抽象出字符设备所具有的的特点。

在任何操作系统下,对字符设备进行IO操作必须遵循底层的最小单位对齐原则,比如对于卷字符设备来讲,每个IO长度只能是扇区的整数倍,如果IO长度没有以扇区为单位对齐(比如513、1500),那么将会收到错误的通知而失败。虽然unux类操作系统下的字符设备也表现为一个文件,但是这个文件却不像块设备一样可以以任意字节进行IO,因为OS没有为字符设备设置任何缓存(但是存在Queue)

3、裸设备与文件系统之争:

字符设备又称为裸设备,应用程序可以选择使用文件系统听的各项功能进行对文件的IO操作,当然也可以选择直接对裸设备进行IO操作,只不过直接对裸设备操作需要应用程序自行维护数据——扇区 映射以及预读缓存、写缓存、读写优化等。比如数据库类程序自身都有这些功能,所以没有必要再使用文件系统来读写数据。而由于块设备的诸多不便和恶劣性能影响,不推荐直接使用。那基于文件系统的IO和基于裸设备的IO,我们可以着重讨论一下哪个好:

文件系统拥有诸多优点是毋庸置疑的,但是对于某一类程序,FS提供的这些“方便”的功能似乎就显得很有局限性了,比如缓存的管理等,由于文件系统是一个公用平台,同时为多个应用程序提供服务,所以它不可能只为一个应用程序而竭尽全力服务;况且最重要的是,FS不会感知应用程序实际想要什么,而且FS自身的缓存在系统异常当机的时候最容易造成数据的不一致情况发生。其次,使用缓存的IO方式下,对于读请求,系统IO路径中的各个模块需要将数据层层向上层模块的缓存中复制,最后才会被OS复制到用户程序缓存;对于写请求,虽然缓存IO方式下,写数据被OS接收后即宣告完成,但这也是造成Down机后数据丢失的主要原因之一。所以对于大数据吞吐量IO请求,
避免内存中多余的数据复制步骤是必要的(此外还有另外一个原因后续介绍)。但是对于一般程序,是完全推荐使用文件系统进行IO操作的。

另外一个最重要的原因,在使用内核缓存以及文件系统缓存的情况下,容易发生读写惩罚,这事非常严重地浪费。

对于这类IO性能要求非常高而且对缓存要求非常高的程序,他们宁愿自己直接操作底层物理设备,也不愿意将IO交给FS来处理。这类程序典型的代表就是数据库类程序。虽然这些程序也可以使用文件系统来进行IO操作,但是这个选择只会给程序带来一个方面的好处,那就是文件管理会方便,比如可以看到数据文件实实在在的被放在某个目录下,可以直接将数据文件复制出来做备份,将文件系统快照保护等。而选择文件系统所带来的坏处也是不少的,比如最大的劣势就是重复缓存预读,FS预读了数据,数据库程序依然自己维护一个预读缓存,这两个缓存里面势必有很多数据是重复的,增加了许多空间和计算资源开销,而且这些数据不见得都会产生Cache
Hit效果,所以这类程序宁愿使用裸设备自行管理数据存储和数据IO,所带来的唯一缺点就是数据管理很不方便,除了程序自身,其他程序只看到了一块光秃秃的裸设备在那,里面放的什么东西,怎么放得,只有程序自己知道。

4、Directl IO与裸设备之争

有没有一种方法能够结合FS和裸设备带来的优点呢?有的,为了即享受文件系统管理文件的便利同时而又不使用FS层面的缓存,将缓存和IO优化操作全部交给应用程序自行处理,FS只负责做  文件—扇区  的映射操作以及其他文件管理层面的操作,节约内存耗费以及太高处理速度,操作系统内核提供了一类接口,也就是前文汇总出现的FILE_FLAG_NO_BUFFERING参数。当然,这个参数只是Windows内核提供的,其他操作系统也都有类似的参数。这种Bypass系统内核缓存的IO模式称为DIO,即Direct  IO模式。在UNIX类操作系统下,在mount某个FS的时候可以指定“-direct”参数来表示任何针对这个FS的IO操作都将不使用内核路径中任何一处缓存。当然,也可以在应用程序层控制,比如打开文件时给出O_SYNC或者O_DIRECT、FILE_FLAG_NO_BUFFERING之类的参数,那么不管目标FS在mount是给出了何种参数,这个程序的IO都将不使用文件系统缓存。

时间: 2024-10-02 23:19:42

大话存储系列22——存储系统内部IO 中的相关文章

大话存储系列21——存储系统内部IO 上

1.IT系统的IO结构图 2.应用程序层IO 应用层程序是计算机系统内主动发起IO请求的大户,但是要知道,计算机内不止有应用程序可以向底层存储设备主动发起IO请求,其他的,比如文件系统自身.卷管理层自身.适配器驱动层自身等,都可以主动发起IO.当然,只有应用程序发起的IO才可以修改用户实体数据内容,而其他角色发起的IO一般只是对数据进行移动.重分布.校验.压缩.加密等动作,并不会修改用户层面的实际数据内容. 应用程序在读写数据的时候一般是直接调用操作系统所提供的文件系统API来完成文件数据的读写

大话存储系列23——存储系统内部IO 下

5.层与层之间的调度员:IO Manager IO Manager 或称IO Scheduer.每个操作系统都会有这样一个角色,它专门负责上层程序的IO请求,然后将IO请求下发到对应的模块和设备驱动中执行,然后将结果通知给上层程序,当某个程序师徒访问某个文件的时候,它其实并没有和文件系统打交道,而只是在与IO Manager打交道,而只是在与IO Manager 打交道. 1.某时刻,图中的"Subsystem"这里就是指某个应用程序,向OS(System Service)发起了对某个

大话存储系列2——计算机内部的IO世界

1..总线: IO是通过共享一条总线的方式来实现的,总线也就是一条或者多条物理上的导线,每个部件都接到这些导线上,导线上的电位每个时刻都是相等的(这个地方需要注意下,之前没有考虑这个问题),这样总线上的所有部件都会受到相同的信号.也就是说,这条总线是共享的,同一个时刻只能有一个部件在接收或者发送,是全单工的工作模式. 所有的部件按照另一条总线,也就是仲裁总线或者中断总线上给出的信号来判断这个时刻总线可以由哪个部件来使用.产生仲裁总线或者中断电位的可以是CPU,也可以是总线上的其他设备. 2.北桥

大话存储系列1——对存储的初步认识

这篇文章转载自大牛Hellodba,连接如下:http://www.hellodb.net/2009/08/storage.html 那这篇文章开始我的存储之旅,我将会在近期整理出关于存储的更多细节. IOPS IOPS (Input/Output Operations Per Second),即每秒进行读写(I/O)操作的次数,多用于数据库等场合,衡量随机访问的性能.存储端的IOPS性能和主机端的IO是不同的,IOPS是指存储每秒可接受多少次主机发出的访问,主机的一次IO需要多次访问存储才可以

大话存储系列14——集群文件系统

文件系统是操作系统的一个重要组成部分,通过对操作系统所管理的存储空间的抽象,向用户提供统一的.对象化的访问接口,屏蔽对物理设备的直接操作和资源管理. 根据计算环境和所提供功能的不同,文件系统可划分为四个层次,从低到高依次是: 单处理器单用户的本地文件系统,如DOS的文件系统: 多处理器单用户的本地文件系统,如OS/2的文件系统: 多处理器多用户的本地文件系统,如Unix的本地文件系统: 多处理器多用户的分布式文件系统,如Lustre文件系统. 平时大家有很多叫法:什么集群文件系统.san共享文件

大话存储系列5——RAID原理

整理自网络和大话存储2: 1.预备知识:条带化 当多个进程同时访问一个磁盘时,可能会出现磁盘冲突.大多数磁盘系统都对访问次数(每秒的 I/O 操作,IOPS)和数据传输率(每秒传输的数据量,TPS)有限制.当达到这些限制时,后面需要访问磁盘的进程就需要等待,这时就是所谓的磁盘冲突.     避免磁盘冲突是优化 I/O 性能的一个重要目标,而 I/O 性能的优化与其他资源(如CPU和内存)的优化有着很大的区别 ,I/O 优化最有效的手段是将 I/O 最大限度的进行平衡.     条带化技术就是一种

大话存储系列0——LUN 与 LVM 名词解释

在看这篇文章之前,请先参考我的博客:http://blog.csdn.net/cymm_liu/article/details/8656154  中的第4部分:逻辑卷管理,摘录如下: 逻辑卷一旦创建,之后就无法改变,如果开始的时候划分了100G的逻辑盘,但是两年之后,空间使用快满了,但是又不能放到别的磁盘(逻辑盘),因为受上层文件系统的限制,一个文件不可能跨越多个分区来存放.更别提多个磁盘了.如果一个文件超过了这100G,那就死路一条了.. 所以必须有一种机制,在一个逻辑磁盘空间满了的时候,可以

大话存储系列7——文件系统和IO处理流程

1.文件系统 在早期的计算机系统中,每个程序都必须自己管理磁盘,在磁盘中放自己的数据,程序需要直接和磁盘控制器打交道.有多少个程序要利用磁盘,就有多少个磁盘交互启动接口. 在没有文件系统的计算机上,如果一个程序要向磁盘上存储一些自己的数据,那么这个程序只能自己调用磁盘控制器启动(无VM的情况下),或者调用VM提供的接口,对磁盘写数据.而写完数据后,很有可能被其他程序的数据覆盖掉.引入文件系统后,各个程序之间都通过文件系统接口访问磁盘,所有被写入的数据都称为一个文件,有着自己的名字,是一个实体.而

大话存储系列8——磁盘阵列

JBOD(just a Bound Of  Disk)只是一串磁盘:这就是盘柜,里面没有带RAID 控制器. 而我们说的盘阵(磁盘阵列)是指的自带RAID控制器的盘柜.磁盘阵列将内部的磁盘经过外部SCSI接口连接到主机上段的SCSI接口.此时,整个磁盘对于主机来说,就是主机SCSI总线上的一个或者多个设备,具有一个或者多个SCSI ID.所有的逻辑磁盘都以LUN的形势呈现给主机. hp EVA 和 MSA 的区别 接口不一样嘛! EVA 里面的硬盘接口也是光纤的,而MSA就不是了,用的硬盘是SC