目录
索引节点和目录项
虚拟文件系统
文件系统I/O
磁盘
通用块层
磁盘性能指标
参考
文件系统,本身是对纯粹设备上的文件,进程组织管理的机制,组织方式不同,就会形成不同的文件系统
Linux中一切皆文件,普通文件和目录,还有设备快,套接字,管道,都是统一用文件系统来管理的
为了方便管理,Linux文件系统为每个文件都分配两个数据结构,索引节点(index node)和目录项(directory entry),他们主要用来记录文件的元信息和目录结构
其中,目录项是一个内存缓存,而超级块,索引节点,逻辑块,都是存储在磁盘中的持久化数据
索引节点是每个文件的卫衣标志,而目录维护的是文件系统的树状结构,目录项和索引节点的关系是多对一,也就是一个文件可以由多个别名
比如通过硬链接为文件创建的别名,就会对应不同的目录项,不过这些目录项本质上还是链接同一个文件,索引他们的索引节点相同
磁盘读写的最小单位是扇区,512字节,文件系统把连续的扇区组成逻辑块,每次都以逻辑块为最小单位,来管理数据,常见的逻辑块大小为4KB,也就是8个连续的扇区
下图展示了 目录项,索引节点,文件数据的关系
注意
超级块, 存储整个文件系统的状态
索引节点区,存储索引节点
数据块区, 存储文件数据
目录项,索引节点,逻辑块,超级块,构成了Linux文件系统的四大基本要素,为了支持各种不同的文件系统,Linux内核在用户进程和文件系统的中间,又引入了一个抽象层,叫虚拟文件系统VFS(Virtual File System)
VFS定义了一组所有文件系统都支持的数据结构和标准接口,当用户进程和内核中的其他子系统只需要跟VFS提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节
关于Linux文件系统的架构图,文件系统调用,VFS,缓存,文件系统以及块存储之间的关系 如下
这张图,在VFS下方,Linux支持各种各样的文件系统,如Ext4,XFS,NFS等,按照存储位置的不同,这样文件系统可以分为三类
这些文件系统,要先挂在到VFS目录树种的某个子目录(称为挂载点),然后才能访问其中的文件,比如第一类基于磁盘的文件系统,在安装时要先挂载一个根目录/,在根目录下再把其他文件系统(比如其他的磁盘分区,/proc文件系统,/sys文件系统,NFS等)挂载进来
VFS定义了一组所有文件系统都支持的数据结构和标准接口,这样用户和内核中的其他子系统,就只需要跟VFS提供的统一接口进行交换就可以了
为了降低慢速磁盘对性能的影响文件系统又通过页缓存,目录项以及索引节点缓存,缓解磁盘延迟对应用程序的影响
通过df查看磁盘容量
df也可以查看inode
df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 482M 0 482M 0% /dev
tmpfs 493M 0 493M 0% /dev/shm
tmpfs 493M 424K 493M 1% /run
tmpfs 493M 0 493M 0% /sys/fs/cgroup
/dev/vda1 50G 13G 34G 28% /
tmpfs 99M 0 99M 0% /run/user/0
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 123275 350 122925 1% /dev
tmpfs 126117 1 126116 1% /dev/shm
tmpfs 126117 405 125712 1% /run
tmpfs 126117 17 126100 1% /sys/fs/cgroup
/dev/vda1 3276800 253595 3023205 8% /
tmpfs 126117 1 126116 1% /run/user/0
free输出的Cache,是页缓存和可回收Slab缓存的和,可以从meminfo中得到他们的大小
cat /proc/meminfo | grep -E "SReclaimable|Cache"
Cached: 524500 kB
SwapCached: 0 kB
SReclaimable: 45668 kB
具体到每一种Slab缓存,可以查看slabinfo文件,通过下面命令可以得到所有目录项和各种文件系统索引节点的缓存情况
dentry行表示目录项缓存,inode_cache行表示VFS索引节点缓存,其余的则是各种文件系统的索引节点缓存
cat /proc/slabinfo | grep -E '^#|dentry|inode'
# name : tunables : slabdata
ext4_inode_cache 15675 15675 1072 15 4 : tunables 0 0 0 : slabdata 1045 1045 0
mqueue_inode_cache 9 9 896 9 2 : tunables 0 0 0 : slabdata 1 1 0
hugetlbfs_inode_cache 13 13 608 13 2 : tunables 0 0 0 : slabdata 1 1 0
sock_inode_cache 120 120 640 12 2 : tunables 0 0 0 : slabdata 10 10 0
shmem_inode_cache 805 805 688 23 4 : tunables 0 0 0 : slabdata 35 35 0
proc_inode_cache 1433 1488 656 12 2 : tunables 0 0 0 : slabdata 124 124 0
inode_cache 10710 10710 584 14 2 : tunables 0 0 0 : slabdata 765 765 0
dentry 65751 65751 192 21 1 : tunables 0 0 0 : slabdata 3131 3131 0
使用slabtop观察缓存情况,slabtop按c后按照缓存大小排序的结果
Active / Total Objects (% used) : 227740 / 229249 (99.3%)
Active / Total Slabs (% used) : 8713 / 8713 (100.0%)
Active / Total Caches (% used) : 95 / 132 (72.0%)
Active / Total Size (% used) : 55541.06K / 56073.59K (99.1%)
Minimum / Average / Maximum Object : 0.01K / 0.24K / 8.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
15675 15675 100% 1.05K 1045 15 16720K ext4_inode_cache
65856 65856 100% 0.19K 3136 21 12544K dentry
10724 10724 100% 0.57K 766 14 6128K inode_cache
54600 54600 100% 0.10K 1400 39 5600K buffer_head
5320 5320 100% 0.57K 380 14 3040K radix_tree_node
13890 13890 100% 0.13K 463 30 1852K kernfs_node_cache
296 296 100% 4.00K 37 8 1184K kmalloc-4k
slabtop按c后按照活跃对象数排序
Active / Total Objects (% used) : 227766 / 229249 (99.4%)
Active / Total Slabs (% used) : 8713 / 8713 (100.0%)
Active / Total Caches (% used) : 95 / 132 (72.0%)
Active / Total Size (% used) : 55547.56K / 56073.59K (99.1%)
Minimum / Average / Maximum Object : 0.01K / 0.24K / 8.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
65856 65856 100% 0.19K 3136 21 12544K dentry
54600 54600 100% 0.10K 1400 39 5600K buffer_head
15675 15675 100% 1.05K 1045 15 16720K ext4_inode_cache
13890 13890 100% 0.13K 463 30 1852K kernfs_node_cache
12036 11520 95% 0.04K 118 102 472K Acpi-Namespace
10724 10724 100% 0.57K 766 14 6128K inode_cache
8772 8772 100% 0.04K 86 102 344K ext4_extent_status
5320 5320 100% 0.57K 380 14 3040K radix_tree_node
把文件系统挂载到挂载点后,就能通过挂载点,再去访问它管理的文件了,VFS提供了一组标准的文件访问接口,这些接口以系统调用的方式,提供给应用程序使用
比如cat 一个文件,首先调用open()打开一个文件,再调用read()读取内存,最后调用write()把文件内容输出到控制台标准输出中
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf,size_t count);
ssize_t write(int fd, const void *buf, size_t count);
文件读写方式的各种差异,导致I/O的分类有很多,常见的有 缓冲与非缓冲I/O,直接与非直接I/O,阻塞与非阻塞I/O,同步与异步I/O
第一种,根据是否利用标准库缓存,可以把文件I/O分为缓冲I/O与非缓冲I/O
注意,这里的 缓冲,是指标准库内部实现的缓存,比如很多程序到换行时才真正输出,而换行之前是被标准库暂时缓存起来了
第二种,根据是否利用操作系统的页缓存,可以把文件I/O分为直接I/O和非直接I/O
要实现直接I/O,需要在系统调用中指定O_DIRECT标志,不设置默认是非直接I/O
注意,直接I/O和非直接I/O本质上还是和文件系统交互,如果在数据库等场景中,可以跳过文件系统读写磁盘的情况,就是裸I/
第三种,根据应用程序是否阻塞自身运行,可以把文件I/O分为阻塞I/O和非阻塞I/O
比如访问管道或者网络套接字时,设置O_NONBLOCK标志,就表示用非阻塞方式访问,默认是阻塞访问
第四种,根据是否等待项羽结果,可以把文件I/O分为同步和异步I/O
比如访问管道或网络套接字时,设置了O_ASYNC选项后,相应的I/O就是异步I/O,这样内核再通过SIGIO或者SIGPOLL,来通知进程文件是否可读写
以上很多概念都来自网络编程,比如非阻塞I/O,通常跟select/poll配合,用在网络套接字的I/O中
所以Linux一切皆文件的深刻含义,无论是普通文件和块设备,还是网络套接字和管道等,都可以通过统一的VFS接口来访问
磁盘可以是支持华存储的设备,根据存储介质的不同,常见磁盘可以分为机械磁盘,固态磁盘
1.机械磁盘,也成为硬盘驱动器(Hard Disk Driver),通常缩写为HDD,机械磁盘主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中,在读写数据前,需要移动读写磁头,定位到数据所在的磁道,然后才能访问数据
如果I/O请求刚好连续,就不需要磁道寻址,自然可以获得最佳性能,这其实就是我们熟悉的,连续I/O工作原理,与会对应的就是随机I/O,它需要不停的移动磁头,来定位数据位置,所以读写速度就比较慢
2.固态硬盘(Solid State Disk),通常缩写为SSD,由固态电子元器件组成,固态磁盘不需要磁道寻址,所以不管是连续I/O,还是随机I/O的性能,都比机械磁盘要好得多
无论机械盘还是固态磁盘,相同磁盘的随机I/O都要比连续I/O慢很多
机械磁盘和固态硬盘还有一个最小的读写单位
由于每次读写512字节这么小的单位效率很低,所以文件系统会把连续的扇区,组成逻辑块,然后以逻辑快作为最小单位来管理数据,常见的逻辑块大小是4KB,也就是连续8个扇区,或者一个独立的页,都可以组成一个逻辑块
除了按照存储介质分类,另一种常见的分类方法,是按照接口来分类,比如把硬盘分为
不同的接口,往往分配不同的设备名称,比如IDE设备会分配一个hd前缀的设备名,SCSI和SATA设备会分配一个sd前缀的设备名,或者是多块同类型的磁盘,就会按照a,b,长凳的字母顺序来编号
除了磁盘本身的分类外,当把磁盘介入服务器后,按照不同的使用方法,又可以把他们划分为多种不同的架构
最简单的,就是直接作为独立磁盘设备来使用,这些磁盘往往还会根据需要,划分为不同的逻辑分区,每个分区再用数字编号,比如
/dev/sda,还可以分成两个分区
/dev/sda1/,/dev/sda2
另一个比较常用的架构,是把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列,也就是RAID(Redundant Array of Independent Disks),从而可以提高数据访问性能,并且增强数据存储的可靠性
根据容量,性能和可靠性的不同,RAID一般分为多个级别,RAIDO,RAID1,RAID5,RAID10等
网络存储
将这些磁盘组合成一个网络存储集群,再通过NFS,SMB,iSCSI等网络存储协议,暴露给服务器使用
在Linux中,磁盘实际上是作为一个块设备来管理的,也就是以块为单位读写数据,并且支持随机写,每个块设备都会被赋予两个设备号,分别是主,次设备号,主设备号用在驱动程序中,用来区分设备类型,次设备号则是用来给多个同类设备编号
跟虚拟文件系统VFS类型, 为了减小不同块设备的差异带来的影响,Linux通过一个统一的通用块层,来管理不同的快设备
通用块层,处在文件系统和磁盘驱动中间的一个块设备层,主要有两个功能
跟虚拟文件系统的功能类似,向上,为文件系统和应用程序,提供访问块设备的标准接口,向下,把各种异构的磁盘设备抽象为统一的块设备,并提供统一框架来管理这些设备的驱动程序
通用块层还会给文件系统和应用程序发来的I/O请求排队,并通过重新排序,请求合并等方式,提高磁盘读写的效率
对于I/O请求排序的过程,也就是I/O调度,Linux内核支持四种I/O调度算法,分别是NONE,NOOP,CFQ,DeadLine
Linux存储系统的I/O,从上倒下分为三个层次,分别是文件系统层,通用块层,设备层
这三个I/O层的关系如下图,也是Linux存储系统的I/O战全景图
根据这张I/O栈的全景图,可以更清楚的理解,存储系统I/O的工作原理
存储系统的I/O,通常是整个系统最慢的一环,所以Linux通过多种缓存机制来优化I/O效率
比如为了优化文件访问的性能,会使用页缓存,索引节点缓存,目录项缓存等多种缓存机制,以减少对下层设备的直接调用
同样,为了优化块设备的访问效率,会使用缓冲区,来缓存块设备的数据
注意,使用率只考虑有没有I/O,而不考虑I/O的大小,换句话说,当使用率是100%的时候,磁盘依然有可能接受新的I/O请求
不要孤立的去比较某一个非指标,而要结合读写比列,I/O类型(随机还是连续),以及I/O大小综合分析
比如,在数据库,大量小文件等这些类随机读写比较多的场景中,IOPS更能反映系统的整体性能
在多媒体等顺序读写较多的场景中,吞吐量才更能反映系统的整体性能
我们在位应用程序的服务器选型时,要限度磁盘的I/O性能进行基准测试,以便可以准确评估,磁盘性能是否可以满足应用程序的需求,可选的性能测试工具是 fio,来测试磁盘的IOPS,吞吐量以及响应时间等核心指标,在基准测试时,一定要注意跟进应用程序的I/O特点,来具体评估指标
需要测试出,不同I/O大小(一般是512B 到 1MB中间的某若干值)分别在随机读,顺序读,随机写,顺序写等各种场景下的性能情况
用性能工具得到这些指标,可以作为后续分析应用程序性能的依据,一旦发生性能问题,可以把他们作为磁盘性能的极限值,进而评估磁盘I/O的使用情况
iostat是最常用的磁盘I/O性能观测工具,它提供了每个磁盘的使用率,IOPS,吞吐量等各种常见的性能指标,当然这些指标实际上来自 /proc/diskstats
iostat的一些参数解释
Device:
This column gives the device (or partition) name as listed in the /dev directory.
tps
Indicate the number of transfers per second that were issued to the device. A transfer is an I/O request to the device. Multiple logical
requests can be combined into a single I/O request to the device. A transfer is of indeterminate size.
Blk_read/s (kB_read/s, MB_read/s)
Indicate the amount of data read from the device expressed in a number of blocks (kilobytes, megabytes) per second. Blocks are equivalent to
sectors and therefore have a size of 512 bytes.
Blk_wrtn/s (kB_wrtn/s, MB_wrtn/s)
Indicate the amount of data written to the device expressed in a number of blocks (kilobytes, megabytes) per second.
Blk_read (kB_read, MB_read)
The total number of blocks (kilobytes, megabytes) read.
Blk_wrtn (kB_wrtn, MB_wrtn)
The total number of blocks (kilobytes, megabytes) written.
rrqm/s
The number of read requests merged per second that were queued to the device.
wrqm/s
The number of write requests merged per second that were queued to the device.
r/s
The number (after merges) of read requests completed per second for the device.
w/s
The number (after merges) of write requests completed per second for the device.
rsec/s (rkB/s, rMB/s)
The number of sectors (kilobytes, megabytes) read from the device per second.
wsec/s (wkB/s, wMB/s)
The number of sectors (kilobytes, megabytes) written to the device per second.
avgrq-sz
The average size (in sectors) of the requests that were issued to the device.
avgqu-sz
The average queue length of the requests that were issued to the device.
await
The average time (in milliseconds) for I/O requests issued to the device to be served. This includes the time spent by the requests in queue
and the time spent servicing them.
r_await
The average time (in milliseconds) for read requests issued to the device to be served. This includes the time spent by the requests in queue
and the time spent servicing them.
w_await
The average time (in milliseconds) for write requests issued to the device to be served. This includes the time spent by the requests in queue
and the time spent servicing them.
svctm
The average service time (in milliseconds) for I/O requests that were issued to the device. Warning! Do not trust this field any more. This
field will be removed in a future sysstat version.
%util
Percentage of elapsed time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation
occurs when this value is close to 100%.
iostat提供了非常丰富的性能指标,第一列的Device表示磁盘设备的名字,其他各列指标参考下图
上图的这些指标中
在观测指标时,还要结合请求的大小(rareq-sz 和 wareq-sz)一起分析
从iostat并不能直接得到磁盘饱和度,饱和度通常也没有其他简单的观测方法,但可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,跟基准测试的结果(比如通过fio)进行对比,综合评估磁盘的饱和情况
可以通过pidstat观察每个进程的I/O情况,包括如下内容
用户ID(UID)和进程ID(PID)
每秒读取的数据大小(kB_rd/s),单位是KB
每秒发出的写请求数据大小(kB_wr/s),单位是KB
每秒取消的写请求数据大小(kB_ccwr/s),单位是KB
块I/O延迟(iodelay),包括等待同步块I/O和换入块I/O结束的时间,单位是时钟周期
其他I/O工具还有 iotop
它的输出包括了进程的磁盘读写大小总数,磁盘真实的读写大小总数
因为缓存,缓冲区,I/O合并等因素的影响,它们可能并不相等,剩下的部分,是从各个角度来分别表示进程的I/O情况,包括线程ID,I/O优先级,每秒读写磁盘的大小,换入和等待I/O的时钟百分比等
这两个工具是分析磁盘I/O性能时最常用到的
Ext4文件系统的超级块
磁盘相关命令
RAID总结
Linux_Storage_Stack_Diagram