热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux内核io体系之磁盘io

文章目录架构图前言名词解释IO体系VFS层superblockinodedentryfilePageCache层脏页刷盘预读策略映射层通用块层IO调度层块设备驱动层物理设备层FAQ


文章目录

    • 架构图
    • 前言
    • 名词解释
    • IO体系
    • VFS层
      • superblock
      • inode
      • dentry
      • file
    • PageCache层
      • 脏页刷盘
      • 预读策略
    • 映射层
    • 通用块层
    • IO调度层
    • 块设备驱动层
    • 物理设备层
    • FAQ


架构图

在这里插入图片描述
在这里插入图片描述


前言

Linux I/O体系是Linux内核的重要组成部分,主要包含网络IO、磁盘IO等。基本所有的技术栈都需要与IO打交道,分布式存储系统更是如此。本文主要简单分析一下磁盘IO,看看一个IO请求从发起到完成到底经历了哪些流程。


名词解释

Buffered I/O:缓存IO又叫标准IO,是大多数文件系统的默认IO操作,经过PageCache。
Direct I/O:直接IO,By Pass PageCache。offset、length需对齐到block_size。
Sync I/O:同步IO,即发起IO请求后会阻塞直到完成。缓存IO和直接IO都属于同步IO。
Async I/O:异步IO,即发起IO请求后不阻塞,内核完成后回调。通常用内核提供的Libaio。
Write Back:Buffered IO时,仅仅写入PageCache便返回,不等数据落盘。
Write Through:Buffered IO时,不仅仅写入PageCache,而且同步等待数据落盘。


IO体系

我们先看一张总的Linux内核存储栈图片:
在这里插入图片描述
Linux IO存储栈主要有以下7层:
在这里插入图片描述


VFS层

我们通常使用open、read、write等函数来编写Linux下的IO程序。接下来我们看看这些函数的IO栈是怎样的。在此之前我们先简单分析一下VFS层的4个对象,有助于我们深刻的理解IO栈。

VFS层的作用是屏蔽了底层不同的文件系统的差异性,为用户程序提供一个统一的、抽象的、虚拟的文件系统,提供统一的对外API,使用户程序调用时无需感知底层的文件系统,只有在真正执行读写操作的时候才调用之前注册的文件系统的相应函数。

VFS支持的文件系统主要有三种类型:
基于磁盘的文件系统:Ext系列、XFS等。
网络文件系统:NFS、CIFS等。
特殊文件系统:/proc、裸设备等。

VFS主要有四个对象类型(不同的文件系统都要实现):
superblock:整个文件系统的元信息。对应的操作结构体:struct super_operations。
inode:单个文件的元信息。对应的操作结构体:struct inode_operations。
dentry:目录项,一个文件目录对应一个dentry。对应的操作结构体:struct dentry_operations。
file:进程打开的一个文件。对应的操作结构体:struct file_operations


superblock

superblock结构体定义了整个文件系统的元信息,以及相应的操作。


inode

inode结构体定义了文件的元数据,比如大小、最后修改时间、权限等,除此之外还有一系列的函数指针,指向具体文件系统对文件操作的函数,包括常见的open、read、write等,由i_fop函数指针提供。


dentry

dentry是目录项,由于每一个文件必定存在于某个目录内,我们通过路径查找一个文件时,最终肯定找到某个目录项。在Linux中,目录和普通文件一样,都是存放在磁盘的数据块中,在查找目录的时候就读出该目录所在的数据块,然后去寻找其中的某个目录项
在我们使用Linux的过程中,根据目录查找文件的例子无处不在,而目录项的数据又都是存储在磁盘上的,如果每一级路径都要读取磁盘,那么性能会十分低下。所以需要目录项缓存,把dentry放在缓存中加速。

VFS把所有的dentry放在dentry_hashtable哈希表里面,使用LRU淘汰算法。


file

用户程序能接触的VFS对象只有file,由进程管理。我们常用的打开一个文件就是创建一个file对象,并返回一个文件描述符。出于隔离性的考虑,内核不会把file的地址返回,而是返回一个整形的fd。

file对象是由内核进程直接管理的。每个进程都有当前打开的文件列表,放在files_struct结构体中。


PageCache层

在HDD时代,由于内核和磁盘速度的巨大差异,Linux内核引入了页高速缓存(PageCache),把磁盘抽象成一个个固定大小的连续Page,通常为4K。对于VFS来说,只需要与PageCache交互,无需关注磁盘的空间分配以及是如何读写的。

当我们使用Buffered IO的时候便会用到PageCache层,与Direct IO相比,用户程序无需offset、length对齐。是因为通用块层处理IO都必须是块大小对齐的。

Buffered IO中PageCache帮我们做了对齐的工作:如果我们修改文件的offset、length不是页大小对齐的,那么PageCache会执行RMW的操作,先把该页对应的磁盘的数据全部读上来,再和内存中的数据做Modify,最后再把修改后的数据写回磁盘。虽然是写操作,但是非对齐的写仍然会有读操作。

Direct IO由于跳过了PageCache,直达通用块层,所以需要用户程序处理对齐的问题。


脏页刷盘

如果发生机器宕机,位于PageCache中的数据就会丢失;所以仅仅写入PageCache是不可靠的,需要有一定的策略将数据刷入磁盘。通常有几种策略:

手动调用fsync、fdatasync刷盘,可参考浅谈分布式存储之sync详解。
脏页占用比例超过了阈值,触发刷盘。
脏页驻留时间过长,触发刷盘。
Linux内核目前的做法是为每个磁盘都建立一个线程,负责每个磁盘的刷盘。


预读策略

从VFS层我们知道写是异步的,写完PageCache便直接返回了;但是读是同步的,如果PageCache没有命中,需要从磁盘读取,很影响性能。如果是顺序读的话PageCache便可以进行预读策略,异步读取该Page之后的Page,等到用户程序再次发起读请求,数据已经在PageCache里,大幅度减少IO的次数,不用阻塞读系统调用,提升读的性能。


映射层

映射层是在PageCache之下的一层,由多个文件系统(Ext系列、XFS等,打开文件系统的文件)以及块设备文件(直接打开裸设备文件)组成,主要完成两个工作:

内核确定该文件所在文件系统或者块设备的块大小,并根据文件大小计算所请求数据的长度以及所在的逻辑块号。
根据逻辑块号确定所请求数据的物理块号,也即在在磁盘上的真正位置。
由于通用块层以及之后的的IO都必须是块大小对齐的,我们通过DIO打开文件时,略过了PageCache,所以必须要自己将IO数据的offset、length对齐到块大小。

我们使用的DIO+Libaio直接打开裸设备时,跳过了文件系统,少了文件系统的种种开销,然后进入通用块层,继续之后的处理。


通用块层

通用块层存在的意义也和VFS一样,屏蔽底层不同设备驱动的差异性,提供统一的、抽象的通用块层API。


IO调度层

Linux调度层是Linux IO体系中的一个重要组件,介于通用块层和块设备驱动层之间。IO调度层主要是为了减少磁盘IO的次数,增大磁盘整体的吞吐量,会队列中的多个bio进行排序和合并,并且提供了多种IO调度算法,适应不同的场景。

Linux内核目前提供了以下几种调度策略:

Deadline:默认的调度策略,加入了超时的队列。适用于HDD。
CFQ:完全公平调度器。
Noop:No Operation,最简单的FIFIO队列,不排序会合并。适用于SSD、NVME。


块设备驱动层

每一类设备都有其驱动程序,负责设备的读写。IO调度层的请求也会交给相应的设备驱动程序去进行读写。大部分的磁盘驱动程序都采用DMA的方式去进行数据传输,DMA控制器自行在内存和IO设备间进行数据传送,当数据传送完成再通过中断通知CPU。

通常块设备的驱动程序都已经集成在了kernel里面,也即就算我们直接调用块设备驱动驱动层的代码还是要经过内核。

spdk实现了用户态、异步、无锁、轮询方式NVME驱动程序。块存储是延迟非常敏感的服务,使用NVME做后端存储磁盘时,便可以使用spdk提供的NVME驱动,缩短IO流程,降低IO延迟,提升IO性能。


物理设备层

物理设备层便是我们经常使用的HDD、SSD、NVME等磁盘设备了


FAQ

1、write返回成功数据落盘了吗?
Buffered IO:write返回数据仅仅是写入了PageCache,还没有落盘。

Direct IO:write返回数据仅仅是到了通用块层放入IO队列,依旧没有落盘。

此时设备断电、宕机仍然会发生数据丢失。需要调用fsync或者fdatasync把数据刷到磁盘上,调用命令时,磁盘本身缓存(DiskCache)的内容也会持久化到磁盘上。

2、write系统调用是原子的吗?
write系统调用不是原子的,如果有多线程同时调用,数据可能会发生错乱。可以使用O_APPEND标志打开文件,只能追加写,这样多线程写入就不会发生数据错乱。

3、mmap相比read、write快在了哪里?
mmap直接把PageCache映射到用户态,少了一次系统调用,也少了一次数据在用户态和内核态的拷贝。

mmap通常和read搭配使用:写入使用write+sync,读取使用mmap。

4、为什么Direct IO需要数据对齐?
DIO跳过了PageCache,直接到通用块层,而通用块层的IO都必须是块大小对齐的,所以需要用户程序自行对齐offset、length。

5、Libaio的IO栈?
write()—>sys_write()—>vfs_write()—>通用块层—>IO调度层—>块设备驱动层—>块设备

6、为什么需要 by pass pagecache?
当应用程序不满Linux内核的Cache策略,有更适合自己的Cache策略时可以使用Direct IO跳过PageCache。例如Mysql。

7、为什么需要 by pass kernel?
当应用程序对延迟极度敏感时,由于Linux内核IO栈有7层,IO路径比较长,为了缩短IO路径,降低IO延迟,可以by pass kernel,直接使用用户态的块设备驱动程序。例如spdk的nvme,阿里云的ESSD。

8、为什么需要直接操作裸设备?
当应用程序仅仅使用了基本的read、write,用不到文件系统的大而全的功能,此时文件系统的开销对于应用程序来说是一种累赘,此时需要跳过文件系统,接管裸设备,自己实现磁盘分配、缓存等功能,通常使用DIO+Libaio+裸设备。例如Ceph FileStore的Journal、Ceph BlueStore。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • MySQL缓存机制深度解析
    本文详细探讨了MySQL的缓存机制,包括主从复制、读写分离以及缓存同步策略等内容。通过理解这些概念和技术,读者可以更好地优化数据库性能。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文详细分析了JSP(JavaServer Pages)技术的主要优点和缺点,帮助开发者更好地理解其适用场景及潜在挑战。JSP作为一种服务器端技术,广泛应用于Web开发中。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 解读MySQL查询执行计划的详细指南
    本文旨在帮助开发者和数据库管理员深入了解如何解读MySQL查询执行计划。通过详细的解析,您将掌握优化查询性能的关键技巧,了解各种访问类型和额外信息的含义。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • 本文探讨了MariaDB在当前数据库市场中的地位和挑战,分析其可能面临的困境,并提出了对未来发展的几点看法。 ... [详细]
  • 本文详细介绍了网络存储技术的基本概念、分类及应用场景。通过分析直连式存储(DAS)、网络附加存储(NAS)和存储区域网络(SAN)的特点,帮助读者理解不同存储方式的优势与局限性。 ... [详细]
  • MySQL PMM:MyISAM 和 Aria 存储引擎的性能优化
    本文探讨了 MyISAM 和 Aria 存储引擎在 MySQL 中的关键性能指标,包括密钥缓冲区效率、页面缓存读写性能以及事务日志同步策略。通过优化这些参数,可以显著提升数据库的整体性能。 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
author-avatar
mobiledu2502883787
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有