Linux磁盘管理

2021/01/04 Linux

Linux磁盘管理

在处理器解码并执行指令之前,数据总是从磁盘盘片的扇区上被检索到处理器的缓存和它的寄存器中。执行的结果可以被写回到磁盘。

I/O子系统的体系结构

当磁盘执行写入操作时发生的基本操作。假设磁盘上扇区中的文件数据已经被读取到分页缓存。

  • ①一个进程通过write()系统调用请求写一个文件。
  • ②内核更新已映射文件的分页缓存。
  • ③内核线程pdflush/Per-BDI flush将分页缓存刷新到磁盘。
  • ④文件系统层同时在一个bio(block input output)结构(参考1.4.3节)中放置每个块缓冲,并向块设备层提交写请求。
  • ⑤块设备层从上层得到请求,并执行一个I/O电梯操作,将请求放置到I/O请求队列。
  • ⑥设备驱动器(比如SCSI或其他设备特定的驱动器)将执行写操作。
  • ⑦磁盘设备固件执行硬件操作,如在盘片扇区上定位磁头,旋转,数据传输。

Cache

在过去的20年中,处理器性能的改进要超过计算机系统中的其他组件,比如,处理器缓存、总线(bus)、物理内存及磁盘,等等。访问内存和磁盘的速度较慢会限制整个系统的性能,因此系统性能不会因为处理器速度的改进而增强。缓存机制通过在较快的存储器中缓存频繁使用的数据来解决这个问题。减少了访问较慢的存储器的次数。当前计算机系统在几乎所有的I/O组件中都会使用这项技术,比如硬盘驱动器缓存、磁盘控制器缓存、文件系统缓存、每个应用程序处理的缓存,等等。

存储层次结构

由于在CPU寄存器和磁盘之间访问速度有很大的不同,CPU将花费更多的时间等待来自较慢磁盘驱动器的数据,因此明显削弱了一个快速CPU的优势。存储层次结构通过在CPU和磁盘之间放置L1缓存、L2缓存、物理内存和一些其他缓存减少这种不匹配,从而让进程减少访问较慢的内存和磁盘的次数。越是靠近处理器的存储器,越具有较高的速度和更小的容量。

这种技术也可以利用访问局部性原则优势。在更快的存储器上实现更高的缓存命中率,就可能更快地访问数据。

访问局部性

实现更高的缓存命中率是性能改进的关键。为了实现更高的缓存命中率,使用一种被称为参考局部性(locality of reference)的技术。这项技术基于以下原则:

  • 大多数最近使用过的数据,在不久的将来有较高的几率被再次使用(时间局部性)。
  • 驻留在数据附近的数据有较高的几率被再次使用(空间局部性)。

Linux在许多组件中使用这一原则,比如分页缓存、文件对象缓存(索引节点缓存、目录条目缓存,等等)、预读缓冲等。

刷新脏页

进程可以从缓存在内存中的数据副本中检索相同的数据。当一个进程试图改变数据时,进程首先在内存中改变数据。这时,磁盘上的数据和内存中的数据是不相同的,并且内存中的数据被称为脏页(dirty page)。脏页中的数据应该尽快被同步到磁盘上,因为如果系统突然发生崩溃(电源故障)则内存中的数据会丢失。

同步脏数据缓冲的过程被称为刷新。在Linux 2.6.32内核之前(Red Hat Enterprise Linux 5),通过内核线程pdflush将脏页数据刷新到磁盘。在Linux 2.6.32内核中(Red Hat Enterprise Linux 6.x)pdflush被Per-BDI flush线程(BDI = Backing Device Interface)取代,Per-BDI flush线程以flush-MAJOR:MINOR的形式出现在进程列表中。当内存中脏页的比例超过某一个阀值时,就会发生刷新(flush)。

块层

块层处理所有与块设备操作相关的活动。块层中的关键数据结构是bio(block input output)结构。bio结构是在文件系统层和块层之间的一个接口。 当执行写的时候,文件系统层试图写入由块缓冲区构成的页缓存。它通过放置连续的块在一起构成bio结构,然后将其发送到块层。 块层处理bio请求,并链接这些请求进入一个被称为I/O请求的队列。这个链接的操作被称为I/O电梯调度(I/O elevator)。

I/O elevator(I/O电梯调度)

Linux 2.6内核采用了新的I/O电梯调度模型。而Linux 2.4内核中使用的是一种单一的通用I/O电梯调度方法,2.6内核提供4种电梯调度算法的选择。因为Linux操作系统适用的场合很广泛,所以I/O设备和工作负载特性都会有明显的变化。一个笔记本电脑可能与拥有10 000个用户的数据库系统相比有着不同的I/O需求。为了适应这些情况,有4种有效的I/O电梯调度算法供我们选择。

CFQ(Complete Fair Queuing,完全公平队列)

CFQ电梯调度通过为每个进程维护一个I/O队列,从而对进程实现一个QoS(服务质量)策略。CFQ电梯调度能够很好地适应存在很多竞争进程的大型多用户系统。它积极地避免进程饿死并具有低延迟特征。从2.6.18内核发行版开始,CFQ电梯调度成为默认I/O调度器。

CFQ为每个进程/线程单独创建一个队列来管理产生的请求,也就是说每个进程一个队列,各队列之间使用时间片来调度,以此保证每个进程都能分配到合适的I/O带宽。I/O调度器每次执行一个进程的4个请求。

根据系统的设置和工作负载特性,CFQ调度器可以使单个主应用程序变慢。CFQ最适合其上有许多不同程序要进行并发读和写的系统,例如,多媒体应用和桌面系统。

Deadline

Deadline是一种循环的电梯调度(round robin)方法,Deadline算法实现了一个近似于实时的I/O子系统。在保持良好的磁盘吞吐量的同时,Deadline电梯调度既提供了出色的块设备扇区的顺序访问,又确保一个进程不会在队列中等待太久导致饿死。

Deadline调度器为了兼顾这两个方面,引入了4个队列,这4个队列可分为两类,每一类都由读和写两种队列组成。一类队列用来对请求按起始扇区序号进行排序(通过红黑树来组织),称为sort_list;另一类对请求按生成时间进行排序(由链表来组织),称为fifo_list。每当确定了一个传输方向(读或写),系统都将会从相应的sort_list中将一批连续请求调度到请求队列里,具体的数目由fifo_batch来确定。只有遇到三种情况才会导致一次批量传输的结束:1.对应的sort_list中已经没有请求了;2.下一个请求的扇区不满足递增的要求;3.上一个请求已经是批量传输的最后一个请求了。

所有的请求在生成时都会被赋上一个期限值,并且按期限值将它们排序在fifo_list中,读请求的期限时长默认为500ms,写请求的期限时长默认为5s,由此可以看出内核对读请求是十分偏心的,其实不仅如此,在Deadline调度器中,还定义了一个writes_starved,writes_starved默认值为2,可以将其理解为写请求的饥饿线。内核总是优先处理读请求,当饿死进程的次数超过了writes_starved后,才会去考虑写请求。因此,即使一个写请求的期限已经超过,该请求也不一定会被立刻响应,因为读请求的批处理还没完成,即使处理完,也必须等到饿死进程的次数超过writes_starved才有机会被响应。为什么内核会偏袒读请求呢?这是从整体性能上进行考虑的。读请求和应用程序的关系是同步的,因为应用程序要等待读取完毕,方能进行下一步工作,所以读请求会阻塞进程,而写请求则不一样。应用程序发出写请求后,内存的内容何时被写入块设备对程序的影响并不大,所以调度器会优先处理读请求。

Deadline最适合多程序在磁盘上执行大量I/O操作的系统,例如数据库服务器或文件服务器。

NOOP

NOOP表示无操作,其名称也表明了这一点。NOOP电梯调度简单明了。它是一个简单的FIFO队列,不执行任何数据排序。NOOP算法简单地合并相邻的数据请求,所以它增加了少量的到磁盘I/O的处理器开销。NOOP电梯调度假设一个块设备拥有它自己的电梯算法。当后台存储设备能重新排序和合并请求,并能更好地了解真实的磁盘布局时,通常选择NOOP调度,例如,一个企业级SAN或管理程序。该调度方法对SSD也很有用。

Anticipatory

Anticipatory本质上与Deadline一样,但Anticipatory电梯调度在处理最后一个请求之后会等待一段很短的时间,约6ms(可调整antic_expire改变该值),如果在此期间产生了新的I/O请求,它会在每个6ms中插入新的I/O操作,这样可以将一些小的I/O请求合并成一个大的I/O请求,从而用I/O延时换取最大的I/O吞吐量。

Anticipatory一般用在系统执行大量连续读的情况。不要在数据库服务器上使用。在Linux 3.xx内核中已经取消了对Anticipatory的支持。

I/O设备驱动程序

Linux内核使用设备驱动程序得到设备的控制权。设备驱动程序通常是一个独立的内核模块,它们通常针对每个设备(或是设备组)而提供,以便这些设备在Linux操作系统上可用。一旦加载了设备驱动程序,它将被当作Linux内核的一部分运行,并能控制设备的运行。

SCSI

SCSI(Small Computer System Interface,小型计算机系统接口)是最常使用的I/O设备技术,尤其在企业级服务器环境中。SCSI在Linux内核中实现,可通过设备驱动模块来控制SCSI设备。SCSI包括以下模块类型:

  • Upper level drivers(上层驱动程序)。 sd_mod、sr_mod(SCSI-CDROM)、st(SCSI Tape)和sq(SCSI通用设备),等等。它们提供支持一些SCSI设备类型的功能,比如,SCSI CD-ROM、SCSI tape,等等。
  • Middle level driver(中层驱动程序)。 如scsi_mod,其实现了SCSI协议和通用SCSI功能。
  • Low level drivers(底层驱动程序)。 其提供对每个设备的较低级别访问。底层驱动程序基本上是特定于某一个硬件设备的,可提供给某个设备。例如,IBM ServeRAID controller的ips、Qlogic HBA的qla2300、LSI Logic SCSI controller的mptscsih,等等。
  • Pseudo drive(伪驱动程序)。 如ide-scsi,用于IDE-SCSI仿真。 如果一个设备要实现特定的功能,该功能应该在设备固件和低级别设备驱动程序中实现。支持的功能取决于你使用的硬件和你使用的设备驱动程序的版本号。设备自身也支持一些功能。特定的功能通常通过一个设备驱动参数来调整。可以试试在/etc/modules.conf中进行一些性能调优。参考设备和设备驱动程序文档可得到优化的提示和技巧。

RAID与文件系统

当磁盘出现问题时,通常一个较大的性能影响是文件系统元数据怎样在磁盘上存放。这就出现了磁盘条带阵列(RAID 0、RAID 5和RAID 6)。在一个条带阵列上,磁头在移动到阵列中下一个磁盘之前,单个磁盘上写入的数据称为CHUNKSIZE,所有磁盘使用一次它后返回到第一个磁盘。如果文件系统的布局没有匹配RAID的设计,则有可能会发生一个文件系统元数据块被分散到2个磁盘上,导致对2个磁盘发起请求。或者将所有的元数据在一个单独的磁盘上存储,这样如果该磁盘发生故障则可能会导致该磁盘变成热点。 在创建阵列之前设计你的文件系统以匹配RAID阵列的布局。为此需要考虑以下内容:

  • 文件系统使用的块大小。
  • RAID阵列使用的CHUNK大小。
  • RAID阵列中同等磁盘的数量。

块大小

块的大小指可以读取/写入到驱动器的最小数据量,其对服务器的性能可以有直接的影响。如果你的服务器处理很多小文件,那么较小的块将更有效率。如果你的服务器专用于处理大文件,则较大的块可能会提高性能。块的大小在已存在的文件系统上,且在联机状态下是不能更改的。只有重新格式化才能修改块大小。可以使用的块大小有1024B、2048B、4096B,默认为4096 B。

计算文件系统stride与stripe-width

stride是在一个chunk中文件系统块的数量。因此如果你的文件系统块大小为4KB,则chunk大小则为64 KB,那么你的stride将是64KB/4KB=16块。 同样,stripe-width是RAID阵列上一个条带中文件系统块的数量。比如说你有一个3块磁盘的RAID5阵列。按照定义,在RAID5阵列每个条带中有1个磁盘包含奇偶校验内容。想要得到stripe-width,首先需要知道每个条带中有多少磁盘实际携带了数据块,即3磁盘-1校验磁盘= 2数据磁盘。上面我们已经得到2个磁盘中的stride,就是chunk中文件系统块的数量。因此能计算2(磁盘)* 16(stride)= 32(stripe)。 在创建文件系统时可以使用mkfs给定数量:

mke2fs -t ext4 -b 4096 -E stride=16,stripe-width=64 /dev/san/lun1

Search

    微信好友

    博士的沙漏

    Table of Contents