LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

  • cplus

  • leetcode

  • 存储技术

    • 学习大纲
    • 命令ls的执行过程
      • 文件系统中查找文件的过程
      • inode表中包含了哪些内容
        • inode包含的主要信息
        • inode的工作方式
      • 目录项中包括了哪些内容
        • 目录项包含的内容
        • 目录项的管理
        • 1. VFS(虚拟文件系统)层
        • 2. 文件系统层
        • 目录项的结构示例
      • 目录项和inode有什么联系和区别
        • 目录项和inode的联系
        • 目录项和inode的区别
        • 示例
      • ls命令执行过程
        • 1. 用户输入命令
        • 2. Shell 解析并创建进程
        • 3. 子进程执行 `ls`
        • 4. 获取目录信息
        • 5. 输出结果
        • 6. 进程退出
        • 通过该方法的学习效果
    • mmap && write
    • 命令write的执行过程
    • 块
    • VFS层
    • Ceph

    • Daos

  • 分布式系统

  • 计算机网络

  • Linux操作系统

  • Redis

  • 其他

  • 笔记
  • 存储技术
lisheng
2024-09-10
目录

命令ls的执行过程

# 文件系统中查找文件的过程

以EXT4为例子

  1. 查找根目录的inode: Linux系统开始解析路径时,会从根目录的inode开始。根目录的inode号通常是2(在许多Unix和Linux文件系统中保留给根目录)。

  2. 读取根目录inode: 文件系统从inode表中读取inode 2的内容,获取该inode指向的磁盘数据块位置。

  3. 读取目录数据块: 根据inode中指向的磁盘数据块位置,文件系统读取存储根目录内容的磁盘块。根目录的数据块包含多个目录项,每个目录项代表一个文件或子目录。

  4. 匹配文件名: 文件系统线性搜索或使用HTree索引(如果目录项很多)来查找与目标文件名匹配的目录项。找到匹配项后,获取对应的inode号码。

  5. 读取下一级目录的inode: 文件系统使用匹配的目录项中的inode号码来读取下一级目录的inode内容。重复步骤3和4,逐级查找路径中的每一个路径组件。

  6. 找到目标文件目录项: 最终找到目标文件或目录的目录项。此目录项包含目标文件的文件名和对应的inode号码。

  7. 读取文件的inode: 使用找到的inode号码,文件系统从inode表中读取目标文件的inode。该inode包含目标文件的元数据(如文件权限、大小、时间戳等)以及指向数据块的指针。

  8. 获取文件数据: 根据inode中的数据块指针,文件系统可以进一步读取文件的实际数据内容。

  9. 缓存:为了进一步优化性能,Linux系统使用缓存来减少磁盘I/O操作: - dentry cache(目录项缓存): 缓存最近访问过的文件名到inode的映射,加速路径解析。 - inode cache(inode缓存): 缓存最近访问过的inode结构,减少频繁的磁盘读取。


# inode表中包含了哪些内容

在Linux和大多数Unix文件系统(如EXT4、XFS等)中,inode(索引节点)是用于存储文件元数据的关键结构。每个文件和目录在文件系统中都有一个唯一的inode,inode表中每个inode条目包含了该文件或目录的各种元数据信息。具体来说,inode中包含以下主要内容:

# inode包含的主要信息

  1. 文件类型(File Type):

    • 指示该inode代表的文件的类型,如普通文件、目录、符号链接、块设备文件、字符设备文件、管道(FIFO)、套接字等。
  2. 文件权限(File Permissions):

    • 表示文件的访问权限,包括读、写、执行权限。通常用9个比特表示(用户、组、其他用户各占3位)。
  3. 链接计数(Link Count):

    • 指示有多少个目录项(硬链接)指向这个inode。如果一个文件有多个硬链接,这个计数会大于1。当计数降到0时(没有任何目录项指向这个inode),操作系统可以删除文件。
  4. 文件所有者的用户ID(Owner UID):

    • 指示文件的所有者(用户)的用户ID。
  5. 文件所有者的组ID(Group GID):

    • 指示文件所属的用户组的组ID。
  6. 文件大小(Size):

    • 对于普通文件,表示文件的字节大小;对于目录,表示目录条目列表的大小。
  7. 时间戳(Timestamps):

    • 最后访问时间(Access Time, atime): 文件上次被访问的时间。
    • 最后修改时间(Modification Time, mtime): 文件内容上次被修改的时间。
    • 状态更改时间(Change Time, ctime): 文件的元数据(如权限、所有者等)上次被更改的时间。
  8. 指向数据块的指针(Pointers to Data Blocks):

    • 这些指针用于指示文件数据在磁盘上的物理位置。由于文件可能很大,数据可能分散存储,因此inode包含了指向数据块的多个指针。
    • 直接指针(Direct Pointers): inode直接指向文件数据块的指针(通常有12个)。
    • 单级间接指针(Single Indirect Pointer): 指向一个包含数据块指针的块。
    • 双级间接指针(Double Indirect Pointer): 指向一个块,该块中包含指向其他数据块指针块的指针。
    • 三级间接指针(Triple Indirect Pointer): 指向一个块,该块中包含指向双级指针块的指针。
  9. 文件的块大小(Block Size):

    • 表示文件系统中用于存储文件数据的基本单位大小(通常是4KB、8KB等)。该信息通常不在inode中存储,而是由文件系统的超级块(superblock)提供。
  10. 文件的文件系统标志(Flags):

    • 用于控制文件的特殊属性或行为,比如是否是append-only(只能追加写),是否是immutable(不可修改),是否需要进行同步等。
  11. 数据块数量(Number of Blocks):

    • 该文件占用的实际数据块数量。这可能大于文件大小/块大小的结果,因为文件可能占用部分块(称为文件尾碎片)。
  12. 文件系统特定信息(File System Specific Information):

    • 一些文件系统可能会在inode中包含特定的扩展属性或信息,如EXT4中的扩展属性(Extended Attributes)或XFS中的项目ID等。

# inode的工作方式

  • inode与文件名的关系: 文件名和inode是分离的。文件名保存在目录条目(directory entry)中,目录条目将文件名映射到inode编号。这样,文件重命名只是改变目录项,而不会影响inode。
  • inode表: 在文件系统初始化时(如格式化磁盘时),会创建一个固定数量的inode。这些inode存储在inode表中,每个inode都有唯一的编号(inode number)。文件系统使用这些编号来管理文件。
  • 文件删除: 当文件被删除时,文件系统减少相应inode的链接计数。如果链接计数降到0,且没有打开的文件描述符指向该inode,文件系统会释放inode及其指向的数据块。

总结 inode表中的inode条目记录了文件的所有元数据,但不包括文件名。文件名保存在目录项中,目录项将文件名映射到inode编号。通过这种结构,文件系统能够高效地管理和操作文件,同时提供灵活的文件命名和组织方式。


# 目录项中包括了哪些内容

目录项(Directory Entry)是文件系统中用于管理文件和目录结构的基本单元。每个目录项关联一个文件或子目录,包含文件名以及指向文件或目录元数据(如inode)的指针。

# 目录项包含的内容

目录项通常包含以下主要内容:

  1. 文件名(File Name):

    • 文件名是目录项中最直观的信息。它表示当前目录下文件或子目录的名字。文件名的长度和字符集限制依赖于具体文件系统的实现。
  2. inode号码(inode Number):

    • inode号码是一个整数,唯一标识一个文件或目录在文件系统中的inode。通过这个号码,文件系统可以在inode表中找到对应的inode,进而读取文件或目录的元数据(如权限、大小、时间戳等)。
  3. 文件类型(File Type):

    • 有些文件系统的目录项中会显式存储文件类型信息,如普通文件、目录、符号链接等。这种做法有助于快速确定文件类型,减少频繁访问inode的需要。比如,EXT4文件系统就将文件类型信息存储在目录项中。
  4. 记录长度(Record Length):

    • 在一些文件系统中,目录项还包括一个字段表示该目录项的总长度,以便文件系统在目录文件中读取或跳过目录项。记录长度通常用于支持目录项的可变长度(如EXT4文件系统)。
  5. 额外信息(可选的):

    • 根据文件系统的不同,目录项可能还包含其他额外信息,例如DOS文件系统中的短文件名(8.3命名格式)、NTFS中的扩展属性、FAT文件系统中的文件属性等。

# 目录项的管理

目录项的管理和维护主要由文件系统本身负责,而不是VFS。以下是VFS和文件系统之间的关系及它们各自的职责:

# 1. VFS(虚拟文件系统)层

  • 抽象层: VFS是一个内核的抽象层,它提供了一个通用接口,使得操作系统可以支持多种文件系统(如EXT4、NTFS、XFS等)。VFS本身不直接管理磁盘上的数据结构(如inode表、目录项),而是依赖于底层文件系统的实现。

  • 路径解析和目录项缓存(dentry cache): VFS负责处理路径解析,并维护一个名为dentry cache(目录项缓存)的数据结构。dentry cache缓存文件名到inode的映射,目的是加速路径查找和文件系统操作,但它并不直接存储在磁盘上的目录项。

# 2. 文件系统层

  • 实际数据管理: 具体的文件系统(如EXT4、XFS)负责在磁盘上创建、读取、更新和删除目录项。文件系统定义了目录项的格式和存储位置,并提供操作目录项的函数(如创建、删除、读取)。

  • 与VFS的交互: 当VFS层请求目录项信息(例如在路径解析过程中),具体文件系统的实现会从磁盘读取相应目录项,然后返回给VFS。文件系统还会将这些信息放入dentry cache中,以加快后续的文件查找操作。

# 目录项的结构示例

不同文件系统的目录项结构可能会有很大不同。下面是EXT4文件系统目录项的一个简化结构示例:

  • inode (4 bytes): 指向文件或目录的inode号码。
  • record length (2 bytes): 目录项的总长度(字节数)。
  • name length (1 byte): 文件名的长度(字节数)。
  • file type (1 byte): 文件类型(普通文件、目录、符号链接等)。
  • file name (variable length): 文件名(字符数组,长度可变)。

总结

目录项是文件系统中用于管理文件名和inode之间映射的基本结构,包含文件名、inode号码、文件类型等信息。目录项的内容由具体文件系统负责管理和维护,而不是VFS。VFS层通过dentry cache缓存路径解析中的目录项信息以提高性能,但不直接管理磁盘上的目录项数据。文件系统负责在磁盘上组织和管理目录项数据,并与VFS层交互完成文件操作。


# 目录项和inode有什么联系和区别

**目录项(Directory Entry)和inode(索引节点)**是文件系统中两个重要的数据结构,它们相互关联但承担不同的功能和职责。

# 目录项和inode的联系

  1. 关联性:

    • 目录项:每个目录项代表一个文件或子目录,并包含文件名和指向文件元数据的指针(inode号码)。目录项存在于目录文件中,每个目录是一个文件,它的内容就是目录项列表。
    • inode:每个inode存储一个文件或目录的元数据(如文件大小、权限、所有者、时间戳等)。inode不包含文件名,只包含与文件内容和属性有关的信息。
  2. 连接方式:

    • 一个目录项通过inode号码(inode number)链接到对应的inode。在文件系统操作中,当需要访问某个文件时,文件系统首先通过目录项找到该文件的inode号码,然后使用该号码定位inode来获取文件的元数据和数据位置。
  3. 多对一关系:

    • 多个目录项可以指向同一个inode。这意味着多个目录项可以有相同的inode号码,形成所谓的“硬链接”。硬链接是多个文件名指向同一个文件内容的情况,它们共享同一个inode。当创建硬链接时,并不会创建新的文件或inode,只是增加了一个新的目录项指向已有的inode。

# 目录项和inode的区别

  1. 功能不同:

    • 目录项:主要用于文件名到inode号码的映射,是文件系统中用来组织文件和目录结构的机制。目录项解决了文件系统中的命名问题,即将文件名和文件内容(由inode表示)分离,使得文件可以在不同的目录下存在不同的名字(通过硬链接)。
    • inode:主要用于存储文件的元数据和数据块指针,解决文件系统中的数据管理问题。inode包含了文件的所有属性和指向数据块的指针,使得文件系统可以管理和存储文件内容。
  2. 存储位置不同:

    • 目录项:存储在目录文件中,每个目录文件包含它所包含文件和子目录的所有目录项。目录项的数量和布局因具体文件系统而异。例如,在EXT4文件系统中,目录项存储在目录文件的数据块中。
    • inode:存储在文件系统的inode表中,inode表是一个预先分配的区域,用于存放所有的inode。每个文件系统在创建时(如格式化磁盘时)会指定inode表的大小和位置。inode的位置固定,并通过inode号码来定位。
  3. 包含信息不同:

    • 目录项:包含文件名和inode号码(可能还有文件类型)。目录项并不包含文件的属性信息(如大小、权限等),这些信息都存储在inode中。
    • inode:包含文件的元数据(如大小、权限、所有者、时间戳等)和指向数据块的指针。inode不包含文件名,也不存储与命名相关的信息。
  4. 修改时机不同:

    • 目录项的修改:当文件或目录被创建、删除、重命名或移动时,目录项会被修改。例如,重命名文件只是修改目录项中的文件名部分,inode不变。
    • inode的修改:当文件的内容或元数据(如权限、大小、时间戳等)发生变化时,inode会被修改。例如,写入文件会更改inode中的文件大小和数据块指针等信息。

# 示例

假设我们有一个文件系统,其中包含如下文件结构:

/home/user/docs/file1.txt
/home/user/docs/file2.txt
/home/user/file1.txt (硬链接到 /home/user/docs/file1.txt)
1
2
3

在这个例子中:

  • /home/user/docs是一个目录文件,包含两个目录项:file1.txt和file2.txt。每个目录项都包含文件名(file1.txt或file2.txt)和它们各自的inode号码。
  • file1.txt和/home/user/file1.txt是两个目录项,它们指向相同的inode号码。这两个不同的文件路径实际上指向同一个文件,表示为硬链接。文件系统通过inode号码知道它们引用同一文件数据。
  • file2.txt有自己独立的inode号码,指向另一个inode,存储着它的文件元数据和数据位置。

总结 目录项和inode在文件系统中各司其职:目录项负责文件名和inode之间的映射,提供文件系统的命名空间管理;inode则存储文件的元数据和实际数据的位置,负责文件系统的数据管理。它们的结合使得文件系统能够高效地管理和访问文件。

# ls命令执行过程

我们以 ls 命令为例来实践这个学习方法,细化其底层执行过程。通过追踪从用户输入到操作系统执行的每一步,你会看到 ls 的底层执行如何串联起 Linux 系统中的各个模块。

# 1. 用户输入命令

用户在命令行输入 ls,Shell(如 bash)接收到这个命令。这是用户与操作系统的交互起点。

# 2. Shell 解析并创建进程

Shell 会进行如下操作:

  • 查找命令:Shell 会通过查找 $PATH 环境变量中存储的目录,寻找 ls 程序的可执行文件(通常位于 /bin/ls)。

  • Fork:Shell 使用 fork() 系统调用,创建一个子进程。这个子进程的任务是执行 ls 命令。fork() 是进程管理的重要部分,它会在内核中分配新进程的任务结构,并复制父进程的地址空间。

    底层细节:

    • 内核进程管理:当 fork() 被调用时,内核为新进程创建了 task_struct,该结构包含了进程ID、内存空间、打开的文件描述符等信息。

# 3. 子进程执行 ls

fork() 完成后,子进程会通过 execve() 系统调用替换自身的代码和数据段为 ls 程序的代码。这时系统开始执行 ls 的代码。

底层细节:
- **execve()**:该系统调用通过 VFS(Virtual File System)机制查找 `/bin/ls`,加载到内存中并切换为用户态执行。操作系统通过 `load_elf_binary` 加载 ELF 格式的可执行文件,并为它分配地址空间。
- **VFS 作用**:VFS 层对不同文件系统进行抽象处理,通过文件描述符(`fd`)获取文件 inode,然后通过 inode 结构找到文件在磁盘上的位置。

# 4. 获取目录信息

ls 执行的核心功能是读取目录内容,底层使用了 open()、getdents()、stat() 等系统调用:

  • open():使用 open() 系统调用打开目录,这个调用通过 VFS 获取目录的 inode。

  • getdents():ls 使用 getdents() 系统调用来读取目录项,即目录下文件的名称列表。该调用遍历目录文件系统的目录项结构(如 ext4 中的目录项),并将其传递给用户空间。

  • stat():每个文件或目录的详细信息(如权限、大小、最后修改时间等)通过 stat() 获取。stat() 调用通过 VFS 将文件路径映射到具体的 inode,进一步从磁盘中读取相关信息。

    底层细节:

    • open() 的过程:当 open() 被调用时,操作系统需要在 VFS 中找到该目录的 inode。根据 inode 信息,系统定位文件数据在存储设备中的具体位置。
    • getdents() 工作机制:这个系统调用会通过文件系统实现(如 ext4 的 readdir() 函数),获取目录项的索引信息,返回给用户态程序。
    • stat():使用 dentry 缓存和 inode 缓存机制,减少每次访问文件系统时查找 inode 的开销。

# 5. 输出结果

当 ls 收集到文件名及其属性后,调用 write() 系统调用将结果输出到标准输出(通常是终端)。

  • write():该系统调用将数据通过文件描述符写入到终端设备。终端通常是字符设备文件,write() 会通过字符设备驱动程序,将数据送到屏幕。

    底层细节:

    • 终端驱动:write() 调用会通过设备文件(如 /dev/pts/0)将数据传递给终端驱动程序,驱动程序会通过与用户交互的硬件进行显示。

# 6. 进程退出

ls 命令执行完成后,会调用 exit() 退出。此时,操作系统清理子进程的资源:

  • exit():exit() 系统调用通知内核该进程完成任务,内核释放该进程占用的内存和其他资源,同时通过 waitpid() 让父进程(Shell)获知子进程已完成。

    底层细节:

    • 进程调度与清理:内核通过调度器收回进程的 CPU 时间片,并将进程的状态改为 ZOMBIE,等待父进程回收其资源。

# 通过该方法的学习效果

  1. 进程管理理解深化:通过 fork() 和 execve() 的执行,你可以进一步理解进程的创建、执行和调度机制。
  2. 文件系统深入了解:通过 open()、getdents()、stat() 等调用,你可以掌握文件系统如何通过 inode 和 dentry 机制管理目录和文件。
  3. 系统调用细节掌握:通过追踪系统调用如何在 VFS、内核和硬件之间传递,帮助你了解 Linux 是如何处理不同设备的I/O请求的。

这种细化的过程让你不仅仅停留在对命令的简单使用层面,而是可以从操作系统的角度剖析每个细节。这种探索式学习方法会大大增强你对 Linux 系统整体架构的理解。

编辑 (opens new window)
上次更新: 2024/09/13, 11:59:12
学习大纲
mmap && write

← 学习大纲 mmap && write→

最近更新
01
ceph分布式存储-对象存储(RGW)搭建
10-27
02
ceph分布式存储-集群客户端连接
10-27
03
ceph分布式存储-管理crushmap
10-27
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式