ls -l 命令列出的total是什么

当我们在 Linux 或 Unix 系统上使用 ls -l 命令列出目录的内容时,第一行通常会显示一个 total 后跟一个数字。这个数字表示当前目录下所有文件和子目录使用的总块数。

majn@tiger:~/C_Project$ ll -lh
total 100K
drwxrwxr-x 18 majn majn  4.0K  928 11:58 ./
drwxr-x--- 29 majn csser 4.0K  928 13:59 ../
-rwxrwxr-x  1 majn majn  1.5K  810 12:26 a.out*
......

具体来说:

  • 这里的“块”是文件系统存储的基本单位。不同的文件系统和不同的系统配置可能会有不同的块大小,但在很多文件系统上,块的大小是 512 字节、4096 字节或其他大小。

  • total 后面显示的数字是所有列出的文件和目录占用的块数的总和。它考虑了文件系统中的各种因素,如间接块和元数据。

  • ls 命令显示的这个值实际上是 st_blocks 字段的值,它是 stat 结构体中的一个字段。这个字段表示的是文件大小按照块大小(通常是 512 字节)进行四舍五入后的结果。

要注意的是,这个数字不仅仅是简单地将目录下所有文件的大小加起来,因为它还包括了其他文件系统的开销。

例如,如果有两个大小为 100 字节的文件,它们可能各自占用一个 4096 字节的块(取决于文件系统和配置)。因此,ls 显示的 total 可能远大于这两个文件的实际总大小。

stat 结构体

在 UNIX-like 系统中,stat 结构体提供了关于文件的信息。这些信息大多是文件的元数据,如文件大小、权限、所有者、时间戳等。可以使用 stat 系统调用来获取文件的这些信息,并将结果存储在 stat 结构体中。

以下是 stat 结构体的一般形式(取决于特定的平台和版本,字段可能会有所不同):

struct stat {
    
    
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for filesystem I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

下面是这些字段的简要描述:

  • st_dev: 文件所在设备的 ID。
  • st_ino: 文件的 inode 号。每个文件在其所在的文件系统中都有一个唯一的 inode 号。
  • st_mode: 文件的权限和类型。
  • st_nlink: 文件的硬链接数量。
  • st_uid: 文件所有者的用户 ID。
  • st_gid: 文件所属组的组 ID。
  • st_rdev: 如果文件是一个设备文件,则表示设备的 ID。
  • st_size: 文件的大小(以字节为单位)。
  • st_blksize: 为了优化文件系统 I/O 而设置的块大小。
  • st_blocks: 文件所占用的 512 字节大小的块的数量。
  • st_atime: 文件最后被访问的时间。
  • st_mtime: 文件内容最后被修改的时间。
  • st_ctime: 文件状态(如权限或链接数)最后被更改的时间。

在某些系统上,时间戳可能会有更高的精度,例如 st_atim, st_mtim, st_ctim,它们可能是 timespec 结构体类型,提供了纳秒级的精度。

要获取文件的 stat 信息,可以使用 stat() 函数或其变体(如 fstat()lstat())。例如:

struct stat sb;
if (stat("/path/to/file", &sb) == -1) {
    
    
    perror("stat");
    exit(EXIT_FAILURE);
}

上述代码将 “/path/to/file” 的 stat 信息存储在 sb 结构体中。

stat结构体与inode的关系

stat 结构体和 inode 之间的关系非常紧密。简而言之,stat 结构体为程序提供了对 inode 中存储的信息的访问。先来看看 inode 的定义以便更好地理解它们之间的关系。

inode

在 UNIX 和 UNIX-like 系统中,每个文件都有一个与之关联的 inode(索引节点)。inode 包含关于文件的元数据,例如:

  • 文件类型(普通文件、目录、符号链接等)
  • 权限(读、写、执行等)
  • 所有者和组
  • 文件大小
  • 时间戳(访问时间、修改时间、状态更改时间)
  • 文件数据的物理位置信息(指向文件数据块的指针)
  • 硬链接数
  • 其他属性

每个 inode 在文件系统中都有一个唯一的编号,称为 inode 号。

stat 结构体

当使用 stat(), fstat(), 或 lstat() 系统调用时,它们会填充一个 stat 结构体,该结构体包含与指定文件相关的信息。这些信息实际上是从文件的 inode 中取得的。也就是说,stat 结构体提供了一个方便的方式来在程序中访问 inode 的信息。

关系

所以,可以说 stat 结构体是 inode 信息的一个映射或表示,它使得应用程序能够轻松访问 inode 中的数据,而不必直接与底层文件系统交互。每个 stat 结构体字段大多都对应于 inode 中的一个或多个属性。

例如,stat 结构体中的 st_mode 字段表示文件的权限和类型,这些都存储在 inode 中。同样,st_ino 字段表示文件的 inode 号,st_uidst_gid 字段分别表示文件所有者的用户 ID 和组 ID,这些都是直接从 inode 中取得的。

总之,stat 结构体为程序提供了一个接口,使其能够访问和操作 inode 中的信息。


在大多数常见的文件系统中,一个块(或扇区)通常只能由一个文件占用。也就是说,如果一个文件只占用块的一部分,那么这个块的剩余部分通常是浪费的,并不能被另一个文件使用。

举个例子,假设文件系统的块大小为4KB:

  • 如果我们有一个只有1KB大小的文件,它会占用整个4KB的块,剩下的3KB是浪费的。
  • 如果我们有另一个2KB大小的文件,它不会使用前一个文件未使用的那3KB,而是会占用另一个新的4KB的块,其中2KB未使用。

这样的设计简化了文件系统的设计和数据的读取,因为每个块的地址指向一个确定的文件。

然而,需要注意的是有一种称为"尾部合并"或"块共享"的优化技术,可以允许两个文件共享一个块,但这通常在特定的情境下,如某些复制写入 (Copy-On-Write, COW) 文件系统中可能会看到这种情况。

总体而言,尽管技术上可能有办法让多个文件共享一个块,但在大多数传统的文件系统中,这是不允许的,因为它会增加管理复杂性并可能导致性能下降。


Tail Merging & Copy-On-Write

尾部合并或块共享是某些文件系统中使用的技术,尤其是在那些支持复制写入 (Copy-On-Write, COW) 机制的文件系统中。这种技术旨在优化存储使用和提高空间效率。

尾部合并 (Tail Merging)

在许多文件系统中,由于块的大小固定,小文件和文件的尾部数据往往不会完全填充一个完整的块。在不使用尾部合并的传统文件系统中,这会导致存储浪费。

尾部合并的思想是将多个文件的这些"尾部"数据合并到单个存储块中,从而减少因未完全利用的块而造成的空间浪费。

复制写入 (Copy-On-Write, COW)

COW 是一种优化技术,在写入数据之前,不是直接修改原始数据,而是复制原始数据,并在副本上执行修改。这种方法的优点是它提供了一种原子性的写入方式,可以在系统崩溃时保护数据的完整性,并为高效的快照和其他功能提供基础。

尾部合并与 COW 的关系

在某些使用 COW 机制的文件系统中,尾部合并可以很自然地实现。当文件进行修改时,而不是修改原始块,系统可以简单地将新数据和原始块的尾部数据合并到新块中。由于不直接修改原始数据,这种合并操作变得相对简单和高效。

例如,在 Btrfs 这种 COW 文件系统中,当多个小文件或文件尾部数据被写入到同一个数据块中时,可以实现尾部合并。这不仅优化了存储,还减少了因多次小写入操作而导致的性能开销。

总结

尽管尾部合并和 COW 可以提供存储和性能上的优势,但它们也带来了额外的复杂性。例如,当执行文件删除或修改操作时,文件系统必须跟踪和管理块中的哪一部分属于哪个文件,并确保数据的完整性和一致性。但是,在权衡后,这些技术在很多现代文件系统中仍被认为是有价值的,并为提高存储效率和性能提供了有效的手段。


示例

下面,我们通过一个简单的例子来解释尾部合并COW 如何工作。

假设我们有一个文件系统,其块大小为4KB。

场景 1: 不使用尾部合并

  1. 用户创建了三个小文件,分别为 A.txt (1KB),B.txt (2KB) 和 C.txt (1KB)。
  2. 每个文件都会占用一个4KB的块。尽管 A.txt 和 C.txt 只用了块中的1KB空间,但剩余的3KB是不能用于其他文件的。
  3. 结果是,我们用了12KB的空间来存储4KB的数据,有8KB的空间被浪费。

场景 2: 使用尾部合并

  1. 用户再次创建了 A.txt, B.txt 和 C.txt。
  2. 文件系统看到这些文件都很小,所以它把它们的数据都放入一个单独的4KB块中。
  3. A.txt 的1KB数据,B.txt 的2KB数据和 C.txt 的1KB数据都存储在同一个块中,完美地填满了它。
  4. 结果是,我们只用了4KB的空间来存储4KB的数据,没有空间浪费。

场景 3: 使用 COW

  1. 现在假设用户想修改 B.txt,添加1KB的数据。
  2. 在一个常规文件系统中,通常直接在B.txt的块中添加数据。但在使用COW的文件系统中,B.txt的原始数据被复制到一个新块,然后再在新块中进行修改。
  3. 原始的 B.txt 块保持不变,直到操作成功完成并且新的块准备好替换它为止。这确保了在出现问题(例如电源故障)时数据的一致性和完整性。
  4. 一旦修改完成,文件系统的元数据会更新为指向新块,并可能在某个时刻释放原始块以供将来使用。

结合尾部合并和 COW,一个文件系统可以在添加、删除或修改小文件时,有效地利用和管理存储空间,同时确保数据的安全性和完整性。

猜你喜欢

转载自blog.csdn.net/weixin_43844521/article/details/133384120
今日推荐