索引节点和目录项Linux文件系统为每个文件分配了两个数据结构,索引节点(indexnode)和目录项(directoryentry),主要用来记录文件的元数据文件。信息和目录结构索引节点(简称inode)用于记录文件的元数据,如inode号、文件大小、访问权限、修改日期、数据位置等。索引节点与文件一一对应一,它会像文件内容一样持久存储在磁盘上。一个目录项(简称dentry)用于记录文件名、索引节点指针以及与其他目录项的关系。多个关联的目录条目构成了文件系统的目录结构。与索引节点不同,目录项是由内核维护的内存数据结构,因此常被称为目录项缓存。索引节点是每个文件的唯一符号,目录项维护文件系统的树状结构。目录条目和索引节点之间的关系是多对一的。你可以简单的理解为一个文件可以有多个别名。例如,通过硬链接为文件创建的别名对应于不同的目录项,但这些目录项本质上仍然链接到同一个文件,因此它们的索引节点是相同的。索引节点和目录项记录了文件的元数据以及文件之间的目录关系,那么具体来说,文件数据是如何存储的呢?不应该直接写入磁盘吗?其实磁盘读写的最小单位是扇区,但一个扇区只有512B大小。如果每次读写这么小的单元,效率肯定很低。因此,文件系统将连续的扇区组成逻辑块,然后每次都以逻辑块为最小单位来管理数据。常见的逻辑块大小为4KB,由8个连续的扇区组成。目录条目本身是内存中的缓存,而inode是存储在磁盘上的数据。在之前的Buffer和Cache原理中,我提到为了协调慢速磁盘和快速CPU的性能差异,文件内容会被缓存在pagecache中。当磁盘在文件系统中被格式化时,会被分成三个存储区,超级块,索引节点区和数据块区。其中,超级块存储了整个文件系统的状态。索引节点区用于存放索引节点。数据块区用于存放文件数据。内核使用slab机制来管理目录项和索引节点的缓存。您可以通过/proc/slabinfo文件查看所有目录项和各种文件系统索引节点的缓存状态。dentry行代表目录项缓存,inode_cache行代表VFS索引节点缓存,其余为各种文件系统的索引节点缓存[root@VM-4-5-centoslighthouse]#cat/proc/slabinfo|grep-E'^#|dentry|inode'#name:可调参数:slabdataisofs_inode_cache4646704234:tunables000:slabdata220ext4_inode_cache464446441176278:tunables000:slabdata1721720jbd2_inode12812864641:tunables000:slabdata220mqueue_inode_cache16161024164:可调参数000:平板数据110hugetlbfs_inode_cache2424680122:可调参数000:平板数据220inotify_inode_mark10210280511:tunables000:slabdata220sock_inode_cache253253704234:tunables000:slabdata11110proc_inode_cache31683322728224:tunables000:slabdata1511510shmem_inode_cache882882768214:tunables000:slabdata42420inode_cache1468814748656122:tunables000:slabdata122912290dentry2753127531192211:tunables000:slabdata131113110selinux_inode_security1662616626401021:tunables000:slabdata1631630在实际性能分析中,更多时候使用slabtop命令来查找占用内存最多的缓存类型[root@VM-4-5-centoslighthouse]#slabtopActive/TotalObjects(%used):223087/225417(99.0%)活动/总Slabs(使用百分比):7310/7310(100.0%)活动/总缓存(使用百分比):138/207(66.7%)Active/总大小(已用百分比):50536.41K/51570.46K(98.0%)最小/平均/最大对象:0.01K/0.23K/8.00KOBJS主动使用OBJ大小SLABSOBJ/SLAB缓存大小名称292952887998%0.19K1395215580Kdentry2715027150100%0.13K905303620Kkernfs_node_cache261122580298%0.03K204128816Kkmalloc-322301023010100%0.10K590392360Kbuffer_head1662616626100%0.04K163102652Kselinux_inode_security148561476499%0.64K1238129904Kinode_cache98949894100%0.04K97102388Kext4_extent_status66986698100%0.23K394171576Kvm_area_struct55085508100%1.15K204276528Kext4_inode_cache51205120100%0.02K2025680Kkmalloc-164672463099%0.06K7364292Kkmalloc-6445644564100%0.57K326142608KRADIX_TREE_NODE448044804480100%0.06K7064280KANON_VMA_VMA_CHAIN409640964096100%0.01K851232K32KKMALLOC-835703570357035703570100%0.0%144.1442_1144.1442_298298英尺298英尺298英尺298英尺FTRACEFTRACEFTRACEFTRACEFTRACEFTRACEFTRACEFTRACEFTRACEFTRECEproc_inode_cache3003296198%0.19K14321572Kkmalloc-1922898242283%0.09K6346252Kanon_vma2368194482%0.25K14816592Kfilp2080207599%0.50K130161040Kkmalloc-51220162016100%0.07K3656144Kacpi-operand17601760100%0.12K5532220Kkmalloc-1028159615961596100%0.09K3842152Kkmalloc-96147214721472100%0.09k328k328kkmalloc-96147214721472100%0.0%100%逻辑块和超级块构成了Linux文件系统的四大基本要素但是,为了支持各种文件系统,Linux内核在用户进程和文件系统之间引入了一个抽象层,这就是虚拟文件系统VFS(VirtualFileSystem)。VFS定义了一套所有文件系统都支持的数据结构和标准接口。这样,内核中的用户进程和其他子系统只需要与VFS提供的统一接口进行交互,而无需关心各种底层文件系统的实现细节。DiskSequentialIO和RandomIOCommonBlockLayer通用块层实际上是文件系统和磁盘驱动之间的块设备抽象层。它主要有两个作用。第一个功能类似于虚拟文件系统的功能。向上,为文件系统和应用程序提供访问块设备的标准接口;向下,将各种异构的磁盘设备抽象成统一的块设备,并提供统一的框架来管理这些设备的驱动程序。第二个功能,通用块层还对文件系统和应用程序发送的I/O请求进行排队,通过重排序和请求合并提高磁盘读写效率。其中,对I/O请求进行排序的过程就是我们熟悉的I/O调度。事实上,Linux内核支持四种I/O调度算法,分别是NONE、NOOP、CFQ和DeadLine。第一种NONE,更准确地说,不是I/O调度算法。因为它根本不使用任何I/O调度器,它实际上不处理文件系统和应用程序的I/O,常用于虚拟机(此时磁盘I/O调度完全被处理由物理机)。第二种NOOP是最简单的I/O调度算法。它实际上是一个先进先出队列,只做一些基本的请求合并,常用于SSD盘。第三种CFQ(CompletelyFairScheduler),又称CompletelyFairScheduler,是现在很多发行版默认的I/O调度器。它为每个进程维护一个I/O调度队列,根据时间片进行调度,以平均分配每个进程的I/O请求。与进程CPU调度类似,CFQ也支持进程I/O的优先级调度,因此适用于运行大量进程的系统,如桌面环境和多媒体应用。最后的DeadLine调度算法为读写请求创建不同的I/O队列,可以提高机械盘的吞吐量,保证到达deadline的请求优先处理。DeadLine调度算法多用于I/O压力大的场景,比如数据库。磁盘IO性能指标使用率、饱和度、IOPS、吞吐量、响应时间等,这五个指标是衡量磁盘性能的基本指标。利用率是指磁盘处理I/O的时间百分比。使用率过高(比如超过80%)通常意味着磁盘I/O存在性能瓶颈。饱和度是指磁盘处理I/O的繁忙程度。过度饱和意味着磁盘存在严重的性能瓶颈。当饱和度为100%时,磁盘无法接受新的I/O请求。IOPS(Input/OutputPerSecond)是指每秒的I/O请求数。吞吐量是指每秒I/O请求的大小。响应时间是指从发送I/O请求到收到响应的间隔时间。iostatiostat是最常用的磁盘I/O性能观察工具。它提供了各种常见的性能指标,例如每个磁盘的使用率、IOPS和吞吐量。当然这些指标其实来自/proc/diskstatsubuntu@VM-4-5-ubuntu:~$iostat-d-x1#-d-x表示显示所有磁盘I/O指标Linux4.15.0-159-通用(VM-4-5-ubuntu)12/19/2021_x86_64_(2CPU)设备r/sw/srkB/swkB/srrqm/swrqm/s%rrqm%wrqmr_awaitw_awaitaqu-szrareq-szwareq-szsvctm%utilloop00.000.000.000.000.000.000.000.000.0.001.000.000.000.00scd00.530.0011.310.000.000.000.000.000.350.000.0021.330.000.350.02vda16.784.60520.74145.530.005.530.0054.581.231.850.0231.0431.620.180.38r/s每秒发送给磁盘的请求数(组合请求数)每秒发送到磁盘的请求数(组合请求数)每秒从磁盘读取的数据量(inKB)每秒写入磁盘的数据量(inKB)persecondNumbercombinedreadrequestsNumberofcombinedwriterequestspersecond读取请求处理完成的等待时间(包括在队列中的等待时间和实际设备处理时间,单位毫秒)写请求处理完成的等待时间(包括在队列中的等待时间和设备实际处理时间)处理时间,单位毫秒)平均请求队列长度平均读请求Size(inKB)averagereadrequestsize(inKB)averagetimerequiredtoprocessI/Orequests(不包括等待时间,单位毫秒,注意这是推断的数据,不保证完全准确)diskprocessingI/O时间百分比(即使用率,由于可能存在并行I/O,100%不一定表示磁盘I/O包饱和)在这些指标中,需要注意的是:%util是我们前面提到的磁盘I/O的O利用率速度;r/s+w/s为IOPS;rkB/s+wkB/s为吞吐量;r_await+w_await是响应时间观察指标时,不要忘记结合请求的大小(rareq-sz和wareq-sz)。进程I/O观察pidstat$pidstat-d113:39:51UIDPIDkB_rd/skB_wr/skB_ccwr/siodelayCommand13:39:521029160.004.000.000rsyslogd从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完成的时间,以时钟周期为单位。除了用pidstat实时查看之外,根据I/O大小对进程进行排序也是性能分析中常用的方法。为此,我推荐另一个工具,iotop。它是一个类似于top的工具,可以按照I/O大小对进程进行排序,然后找出那些I/O较大的进程$iotopTotalDISKREAD:0.00B/s|总磁盘写入:7.85K/s实际磁盘读取:0.00B/s|实际磁盘写入:0.00B/sTIDPRIOUSERDISKREADDISKWRITESWAPINIO>COMMAND15055be/3root0.00B/s7.85K/s0.00%0.00%systemd-journald