Linux之ext文件系统

Linux之ext文件系统
1.目录与文件名我们再使用linux中用的都是文件名而不是inode,那么这是怎么实现的???inode 内部不存储文件名inode 只保存文件元数据权限、大小、时间戳、磁盘块指针。文件名存放在目录文件的数据块中。目录本质依然是文件。 普通文件的数据块存放业务数据目录文件的数据块专门存储一张映射表文件名 ---- inode编号。硬链接原理依托这个结构 多个不同文件名指向同一个 inode就是硬链接。删除文件rm本质是删掉目录里一条文件名-inode映射只有 inode 引用计数归 0磁盘数据才会被回收。.与..目录创建时自动写入两条记录.指向自身 inode、..指向父目录 inode也就是内核自动维护的目录硬链接。用户无法手动创建目录硬链接防止环路死循环。通俗理解inode 是文件本体目录里的文件名只是找到本体的 “标签”。一个本体可以贴多个标签硬链接。关键问题目录 r/w/x 权限特殊含义普通文件权限r读取内容w修改内容x执行文件目录权限完全不一样r 权限可以读取目录内的映射表可以执行 ls看到有哪些文件名w 权限允许修改目录内映射表 也就是新建文件、删除文件、重命名文件修改映射条目x 权限允许穿越目录进入目录访问目录内文件对应的 inode# 给所有者增加执行权限 chmod ux test.sh # 取消其他用户写权限 chmod o-w log.txt # 设置所有人只读 chmod ar file # 用户读写组读其他无权限 chmod urw,gr,o fileeg执行命令cat test.cshell 接收字符串test.c操作系统去当前目录文件的数据块查找映射表匹配字符串test.c拿到对应的 inode 号根据 inode 编号在磁盘找到对应的 inode 结构体读取 inode 里的数据块指针找到磁盘数据块将数据读出打印到屏幕。#include stdio.h #include string.h #include stdlib.h #include dirent.h #include sys/types.h #include unistd.h int main(int argc, char *argv[]) { if (argc ! 2) { fprintf(stderr, Usage: %s directory\n, argv[0]); exit(EXIT_FAILURE); } DIR *dir opendir(argv[1]); if (!dir) { perror(opendir); exit(EXIT_FAILURE); } struct dirent *entry; while ((entry readdir(dir)) ! NULL) { // 跳过 . 和 .. if (strcmp(entry-d_name, .) 0 || strcmp(entry-d_name, ..) 0) { continue; } printf(Filename: %s, Inode: %lu\n, entry-d_name, (unsigned long)entry-d_ino); } closedir(dir); return 0; }2.路径解析问题打开当前工作目录文件查看当前工作目录文件的内容?当前工作目录不也是文件吗我们访问当前工作目录不也是只知道当前工作目录的文件名吗要访问它不也得知道当前工作目录的inode吗答案1所以也要打开当前工作目录的上级目录额....上级目录不也是目录吗不还是上面的问题吗答案2所以类似递归需要把路径中所有的目录全部解析出口是/根目录。最终答案3而实际上任何文件都有路径访问目标文件什么是路径解析当我们调用open(/a/b/c.txt)内核无法直接通过字符串找到文件必须逐层拆解路径、逐级查询目录这个过程就是路径解析。完整流程示例/home/user/test.log定位根目录/的 inode读取根目录的数据块遍历目录映射表查找名称home得到 home 目录 inode读取 home 目录的数据块查找user拿到 user 目录 inode读取 user 目录数据块查找test.log获取目标文件 inode使用 inode 访问文件元数据与文件数据块。然而每次找文件位置都这样是很不方便的,会花费大量的时间,所以系统会将文件路径缓存起来,但是很多很多的缓存就要先描述再组织3.路径缓存dentry 全称 Directory Entry目录项。磁盘上的 dentry目录文件数据块内文件名-inode号的原始记录。内存中的 dentry 缓存内核把常用目录项加载进内存构建内存哈希表。struct dentry { atomic_t d_count; unsigned int d_flags; /* protected by d_lock */ spinlock_t d_lock; /* per dentry lock */ struct inode *d_inode; /* Where the name belongs to - NULL is * negative */ /* * The next three fields are touched by __d_lookup. Place them here * so they all fit in a cache line. */ struct hlist_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct list_head d_lru; /* LRU list */ /* * d_child and d_rcu can share memory */ union { struct list_head d_child; /* child of parent list */ struct rcu_head d_rcu; } d_u; struct list_head d_subdirs; /* our children */ struct list_head d_alias; /* inode alias list */ unsigned long d_time; /* used by d_revalidate */ struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ void *d_fsdata; /* fs-specific data */ #ifdef CONFIG_PROFILING struct dcookie_struct *d_cookie; /* cookie, if any */ #endif int d_mounted; unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ };缓存带来的优化下次访问同一文件不需要再次读取磁盘目录块 直接在内存 dentry 哈希表中匹配文件名快速拿到 inode省去大量磁盘 IO。dentry 生命周期活跃文件dentry 长期驻留内存内存紧张时内核回收不常用的 dentry 缓存文件删除 / 重命名对应 dentry 失效。重谈inode编号和块号a.inode编号和块号:不是组内有效,是整个分区内有效,唯一(inode)b.再一个分区内,一个文件系统内部有多少个数据块,inode是固定的,提前设计好的根据块号和inode通过计算可以找到相应的组如果我要访问我当前的文件内容分或者属性,首先要打开当前目录,访问当问目录的数据块eg:以读取/home/test/log.txt举例从根目录/开始找到根目录 inode读取根目录 inode 对应目录数据块目录块内存放一张「文件名 → inode 编号」映射表dentry 原始磁盘存储形式在根目录块中查找名称home拿到home目录 inode读取 home 目录 inode 的数据块查找test拿到 test 目录 inode读取 test 目录数据块查找log.txt得到目标文件 inode 编号读取log.txt的 inodeinode 中存放文件元数据大小、权限、时间inode 内部磁盘块指针指向存放真实文本内容的数据块读取数据块最终拿到文件内容。1. 你怎么知道文件属于哪一个文件系统super_block超级块每一块磁盘分区被挂载后内核会创建一个超级块实例struct super_block代表一个独立文件系统ext4/xfs/tmpfs 等。完整链路挂载流程mount命令读取分区超级块在内核生成super_block对象关联关系inode-i_sb每个 inode 内部自带指针指向所属文件系统的超级块dentry-d_inode→inode→super_blockstruct file通过 dentry/inode 最终可以追溯到super_block。超级块记录信息文件系统类型ext4、tmpfs、btrfs挂载点、块大小、最大 inode 编号、文件系统专属操作函数集用户态验证方式statfs()系统调用可以获取文件所在文件系统信息命令df 文件路径、mount查看挂载列表。inode 绑定 super_block顺着 inode 就能定位文件归属的文件系统。2. 存储大文件问题问题本质inode 不直接存储文件内容只保存元数据inode 中存放磁盘块指针用来找到存放文件真实数据的磁盘块。 ext 经典三级索引模型直接块直接指向数据块适合小文件寻址速度最快一级间接块块内存放一批数据块编号二级间接块指向一级间接块列表三级间接块超大文件使用#define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK 1)硬链接硬链接是什么我们观察到他们的inode编号是一样的,也就是说明,这两个文件其实是同一个文件mylog就是原来文件的别名,所以建立硬链接的本质就是再当前目录下新建一个新的字符串(文件名)和目标文件的映射关系蓝色框内就是说明有几个文件指向他为什么要有??删除文件要删除文件名和inode的映射关系,所以硬链接数为1,时删除文件才真正把文件删除1.删除旧备份不会损坏现存数据每日轮换备份示例保留最近 7 天备份。 当需要清理最旧一天备份时 我们只需要删除旧备份目录下的所有硬链接unlink。 只要其他备份快照依然持有 inode 硬链接计数磁盘上真实文件数据不会被删除。 完美实现滚动清理旧备份不会误删其他快照里的文件。现在将其删除但是不用害怕,文件的备份依然存在为什么目录的硬链接是2,普通文件硬链接是11. 普通文件log链接数 1执行touch log只产生1 条 dentry当前目录下的文件名log。 没有额外名字指向这个 inode所以计数 1。 只有手动ln log log_hard创建硬链接数值才会上涨。2. 新建目录dir链接数 2mkdir dir自动生成两个 dentry 指向 dir 的 inodedir上层目录homework里的目录名.dir 目录内部的当前目录符号。所以刚创建完毕链接计数直接是 2。我们再dir下面在创建ddir发现文件dir的硬链接变多了,因为ddir的隐藏文件..是dir的硬链接所以. .. 本质上是硬链接硬链接不能给目录建立,防止用户行程环形路径,进入死循环.软连接ln -s test.c test-soft是什么软连接是一个独立的文件文件内容属性软连接文件的内容存的是指向目标文件的路径为什么现在我的可执行文件myexe再很深的目录下面这样执行就不方便所以我们可以采用软连接,达到便捷的效果软连接类似于windows下面的快捷方式