
Linux 文件系统一、文件系统底层核心概念Linux 中一个文件由三部分组成目录项dentry索引节点inode数据块data。1. inode索引节点本质结构体存储文件的元数据属性信息。存储内容权限、类型、大小、时间戳、用户/组 ID、磁盘块位置指针等。存储位置绝大多数 inode 存储在磁盘上少量常用或近期使用的 inode 会被缓存到内存中以提高访问速度。2. dentry目录项本质结构体存在于内存中由内核缓存用于加速路径查找。核心成员{ 文件名, 指向对应 inode 的指针, ... }。作用将文件名与 inode 关联起来。注意文件内容data保存在磁盘盘块中dentry 只负责“名字映射”。3. 文件系统本质一组规则规定文件的存储方式及读取方法。文件系统在磁盘格式化时指定。常见类型fat32、ntfs、exfat、ext2、ext3、ext4等。不同的文件系统 构建的 innode和dentry也不同4. 硬链接与软链接硬链接多个 dentry 指向同一个 inode。删除一个 dentry 不影响数据直到链接数归零。ls -l的第二列数字可以查看软链接符号链接独立文件拥有自己的 inode内容是指向目标文件的路径。若目标被删除软链接失效“悬空链接”。为什么 dentry 要独立于 inode—— 为了实现文件共享多个文件名可指向同一物理文件数据避免数据冗余也方便文件“重命名”仅修改 dentry 中的文件名无需移动数据。二、获取文件属性元数据1.stat函数 —— 获取文件属性从 inode 结构体中将属性信息复制出来。#includesys/stat.h#includeunistd.hintstat(constchar*path,structstat*buf);参数path– 文件路径buf– 传出参数接收属性结构体。返回值成功返回0失败返回-1并设置errno。特点会穿透符号链接即追踪到链接指向的源文件。2.lstat函数 —— 获取文件属性不穿透链接intlstat(constchar*path,structstat*buf);返回值同stat。特点不会穿透符号链接查看的是链接文件本身的属性不追踪到源文件。3. 文件类型判断宏函数从st_mode成员中提取高 4 位判断推荐使用以下 POSIX 宏S_ISREG(m)– 普通文件S_ISDIR(m)– 目录S_ISCHR(m)– 字符设备S_ISBLK(m)– 块设备S_ISFIFO(m)– 命名管道S_ISLNK(m)– 符号链接S_ISSOCK(m)– 套接字代码示例#includestdio.h#includesys/stat.hintmain(intargc,char*argv[]){structstatsb;if(argc2)return1;if(lstat(argv[1],sb)-1){perror(lstat);return1;}if(S_ISREG(sb.st_mode))printf(普通文件\n);elseif(S_ISDIR(sb.st_mode))printf(目录\n);elseif(S_ISLNK(sb.st_mode))printf(符号链接\n);elseprintf(其他类型\n);return0;}三、文件数据操作修改、截断、链接与删除1.truncate/ftruncate—— 截断或扩展文件长度常用于将文件大小归零或拓展文件大小无需使用lseekwrite。#includeunistd.h#includesys/types.hinttruncate(constchar*path,off_tlength);intftruncate(intfd,off_tlength);参数path/fd指定文件length为目标大小字节。返回值成功返回0失败返回-1并设置errno。注意若原文件大于length尾部数据被丢弃若小于则扩展并填充空字节‘\0’。2.link—— 创建硬链接增加目录项#includeunistd.hintlink(constchar*oldpath,constchar*newpath);返回值成功返回0失败返回-1并设置errno。注意oldpath必须存在newpath不能已存在。路径中的目录必须存在。3.unlink—— 删除目录项硬链接数减 1#includeunistd.hintunlink(constchar*pathname);返回值成功返回0失败返回-1并设置errno。核心机制将文件的硬链接数st_nlink减 1。若减至0且没有进程打开该文件操作系统择机释放磁盘空间。若减至0但仍有进程打开该文件文件内容不会被立即释放直到所有打开该文件的进程关闭它或进程终止系统才会回收。“隐式回收”系统资源当进程结束时所有被该进程打开的文件描述符会自动关闭申请的内存空间也会自动释放应用场景临时文件#includefcntl.h#includeunistd.hintmain(){intfdopen(temp.txt,O_CREAT|O_RDWR,0666);if(fd-1)return1;unlink(temp.txt);// 立即删除目录项但文件数据仍可读写write(fd,Hello,5);// 数据正常写入close(fd);// 关闭后文件真正被释放return0;}四、目录操作1. 工作目录相关函数getcwd– 获取进程当前工作目录绝对路径。#includeunistd.hchar*getcwd(char*buf,size_tsize);成功返回buf失败返回NULL。chdir– 修改进程当前工作目录。intchdir(constchar*path);成功返回0失败返回-1。代码示例charbuf[256];if(getcwd(buf,sizeof(buf))!NULL){printf(当前目录: %s\n,buf);}chdir(/tmp);printf(切换后目录: %s\n,getcwd(buf,sizeof(buf)));2. 目录遍历相关函数库函数类似 FILE* 操作opendir– 打开目录流。#includedirent.hDIR*opendir(constchar*name);成功返回目录流指针失败返回NULL。closedir– 关闭目录流。intclosedir(DIR*dirp);成功返回0失败返回-1。readdir– 读取目录项每次返回一条即文件夹下多个文件名每次返回一个文件名。structdirent*readdir(DIR*dirp);成功返回指向struct dirent的指针失败或读取结束返回NULL。struct dirent重点成员ino_t d_ino– inode 编号。char d_name[256]– 文件名最常用。unsigned char d_type– 文件类型部分系统支持非完全可移植。注意readdir返回NULL有两种情况① 读取完毕② 出错。应检查errno以区分errno0;structdirent*entry;while((entryreaddir(dir))!NULL){printf(%s\n,entry-d_name);}if(errno!0){perror(readdir error);}代码示例简单 ls#includedirent.h#includestdio.hintmain(){DIR*diropendir(.);if(!dir)return1;structdirent*entry;while((entryreaddir(dir))!NULL){if(entry-d_name[0]!.){// 不打印隐藏文件printf(%s ,entry-d_name);}}closedir(dir);return0;}3. 递归遍历目录利用stat/lstat判断文件类型若S_ISDIR为真则递归调用进入该子目录遍历。五、文件与目录权限详解目录本质上也是文件其内容是该目录下所有子文件的 dentry。权限位对普通文件的作用对目录的作用r读可查看文件内容cat, more, less可浏览目录下的文件名列表ls, treew写可修改文件内容vi, 可创建、删除、重命名该目录下的文件touch, mv, rmx执行可执行该文件./filename可进入该目录cd。若无可执行权限即使有 r 权限也无法查看文件详情。六、文件描述符重定向与复制1.dup函数 —— 复制文件描述符#includeunistd.hintdup(intoldfd);作用生成一个新的文件描述符指向与oldfd相同的内核文件表项共享文件偏移量和状态。返回值成功返回当前可用最小的文件描述符编号失败返回-1。2.dup2函数 —— 重定向文件描述符到指定值intdup2(intoldfd,intnewfd);作用将newfd重定向到oldfd所指向的文件。若newfd已被打开则先关闭它。返回值成功返回newfd若oldfd无效调用失败返回-1并关闭newfd。重定向标准输出到文件#includefcntl.h#includeunistd.h#includestdio.hintmain(){intfdopen(output.txt,O_CREAT|O_WRONLY|O_TRUNC,0666);if(fd-1)return1;dup2(fd,STDOUT_FILENO);// 将标准输出(1)重定向到文件close(fd);// 可关闭原fd因为副本已生效return0;}dup2(old, new)即new 指向 old相当于new old的指向。