本文主要是介绍用户层read write io命令到NVMe SSD全流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
引言
以EXT-4以及read io cmd为例,介绍从User Space -> File System -> NVMe Driver -> PCIe Controller -> NVMe Controller -> SSD Firmware(Flash Translate layer) 的基本流程
明确几个要点:
- inode number(inode号)是单纯一个整形数,一般存放在目录文件的data block里。
- inode是struct inode对象,里面存放了跟文件相关的所有信息,结构体成员代码段1所示。
- struct file是在打开文件时VFS分配的,fd(文件描述符)用指示struct file结构体对象,两者均属于进程。而inode不属于进程,一个文件对应一个inode。
代码段1
struct ext4_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size_lo; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Inode Change time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks_lo; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
__le32 l_i_version;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl_lo; /* File ACL */
__le32 i_size_high;
__le32 i_obso_faddr; /* Obsoleted fragment address */
union {
struct {
__le16 l_i_blocks_high; /* were l_i_reserved1 */
__le16 l_i_file_acl_high;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
__le16 l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
__le16 l_i_reserved;
} linux2;
struct {
__le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */
__le16 m_i_file_acl_high;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
__le16 i_extra_isize;
__le16 i_checksum_hi; /* crc32c(uuid+inum+inode) BE */
__le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
__le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */
__le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
__le32 i_crtime; /* File Creation time */
__le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
__le32 i_version_hi; /* high 32 bits for 64-bit version */
__le32 i_projid; /* Project ID */
};
整体流程
假设要对根目录下的1.txt文件进行读命令
- 首先是挂载文件系统。文件系统挂载时,会将一部分metadata存放在内存中,其中包括Super block, Group Block信息以及"/"根目录文件的inode number
- 根据"/“根目录文件的inode number以及metadata可以计算出”/"文件的inode,并且通过读取inode里面的i_block找到对应的逻辑块号以及文件的长度
- 根据逻辑块号向NVMe SSD发起read请求,得到数据块的真实内容,这里假设该数据块已经缓存在文件系统的page cache里,下文对1.txt进行读命令时再详细分析此过程。
- 在data block中根据文件名(1.txt)进行索引,找到1.txt对应的inode number
- 根据1.txt的inode number以及metadata可以计算出"1.txt"文件的inode,并且通过读取inode里面的i_block找到对应逻辑块号和长度
- 将此命令传到block io layer,并根据block io layer进行调度,最后再给到NVMe驱动层。
- 驱动层会根据NVMe控制器的特性,将此read请求进行分割,比如一次只读取64个block,然后同时发起多个read请求
- 将请求写入多个SQ队列
- 通过PCIe Write TLP向NVMe控制器的每个sq doorbell寄存器写入新增的请求数量,通知SSD来主机端的SQ队列拿请求
- SSD拿到请求后解析命令,并将逻辑地址(LBA)通过FTL转化成物理块地址(PPA)
- 通过Flash Controller读取对应的CHIP,DIE,Plane,Block中的page。
- 将完成状态写入主机端的CQ队列
- 通过中断告诉主机端命令已经完成
- 主机端通过读取CQ队列获取命令完成状态
这篇关于用户层read write io命令到NVMe SSD全流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!