鱼C论坛

 找回密码
 立即注册
查看: 2288|回复: 1

[学习笔记] 007-文件系统

[复制链接]
发表于 2018-11-2 21:44:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 moc 于 2018-11-2 21:44 编辑

1. 什么是文件系统
        文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法
        操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。
        文件系统由三部分组成:
                ① 文件系统的接口;       ② 对对象操纵和管理的软件集合;    ③ 对象及属性。
        从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。,控制文件的存取,当用户不再使用时撤销文件等。
2. ext2文件系统
        一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如某种mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息。下图是一个磁盘分区格式化成ext2文件系统后的存储布局。
linux-sys.zh.bmp

1. 块(Block)
         块(Block)是文件系统中存储的最小单位,一个块究竟多大是在格式化时确定的,例如mke2fs的-b选项可以设定块大小为1024、2048或4096字节。
2. 启动块(Boot Block)
        启动块(Boot Block)的大小是确定的,就是1KB,启动块是由PC标准规定的,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。
3. 块组(Block Group)
        启动块之后才是ext2文件系统的开始,EXT2会将整个分区空间分为一个个BlockGroup(块组),这些块组的大小都是一致的,每个磁盘空间中至少有一个块组。大致就相当于EXT2这个“管家”会把我们的家先分成一个个大小一致的“房间”。每个块组又由超级块等部分组成,如上图所示。
4. 超级块(Super Block)
        超级块(Super Block)描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount(挂载)的时间等等。超级块在每个块组的开头都有一份拷贝。
5. 块组描述符表(GDT,Group Descriptor Table)
        块组描述符表(GDT,Group Descriptor Table)由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符。
        每个块组描述符(Group Descriptor)存储一个块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。
        和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。通常内核只用到第0个块组中的拷贝,当执行e2fsck检查文件系统一致性时,第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。
6. 块位图(Block Bitmap)
        块位图(Block Bitmap)一个块组中的块是这样利用的:数据块存储所有文件的数据,比如某个分区的块大小是1024字节,某个文件是2049字节,那么就需要三个数据块来存,即使第三个块只存了一个字节也需要占用一个整块;
        超级块、块组描述符表、块位图、inode位图、inode表这几部分存储该块组的描述信息。
        那么如何知道哪些块已经用来存储文件数据或其它描述信息,哪些块仍然空闲可用呢?块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个bit代表本块组中的一个块,这个bit为1表示该块已用,这个bit为0表示该块空闲可用。
7. inode位图(inode Bitmap)
        inode位图(inode Bitmap):和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。
8. inode表(inode Table)
        inode表(inode Table):我们知道,一个文件除了数据需要存储之外,一些描述信息也需要存储,例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是ls -l命令看到的那些信息,这些信息存在inode中而不是数据块中。每个文件都有一个inode,一个块组中的所有inode组成了inode表。
9.数据块(Data Block)
        数据块(Data Block)根据不同的文件类型有以下几种情况:
                ① 对于常规文件,文件的数据存储在数据块中。
                ② 对于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外,ls -l命令看到的其它信息都保存在该文件的inode中。
                        注意这个概念:目录也是一种文件,是一种特殊类型的文件。
                ③ 对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
                ④ 设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
etx2文件剖析.png
目录中记录项文件类型:
编码        文件类型
0 Unknown
1 Regular file
2 Directory
3 Character device
4 Block device
5 Named pipe
6 Socket
7 Symbolic link

补充: 数据块寻址
linux-sys.zh.bmp

索引项Blocks[13]指向两级的间接寻址块,最多可表示(b/4)2+b/4+12个数据块,对于1K的块大小最大可表示64.26MB的文件。索引项Blocks[14]指向三级的间接寻址块,最多可表示(b/4)3+(b/4)2+b/4+12个数据块,对于1K的块大小最大可表示16.06GB的文件。
3.文件操作函数
1. stat 函数
原型: int stat(const char *path, struct stat *buf);
作用: 获取文件属性(从inode上获取)
特点: 能够穿透(跟踪)符号链接
文件属性结构体:
  1. struct stat {
  2.         dev_t                st_dev;                // 文件设配编号
  3.         ino_t                st_ino;                // 节点
  4.         mode_t                st_mode;        // 文件类型和存取的权限
  5.         nlink_t                st_nlink;        // 连到该文件的硬链接数目,刚建立的文件值为1
  6.         uid_t                st_uid;                // 用户ID
  7.         gid_t                st_gid;                // 组ID
  8.         dev_t                st_rdev;        // (设备类型)若此文件为设备文件,则为其设备编号
  9.         off_t                st_size;                // 文件字节数(文件大小)
  10.         blksize_t        st_blksize;        // 块大小(文件系统I/O 缓冲区大小)
  11.         blkcnt_t        st_blocks;        // 块数
  12.         time_t                st_atime;        // 最后一次访问时间
  13.         time_t                st_mtime;        // 最后一次修改时间
  14.         time_t                st_ctime;        // 最后一次改变时间(指修改属性即inode内容)
复制代码
2. lstat 函数
原型: int lstat(const char *path, struct stat *buf);
作用: 与stat一样
特点: 不能够穿透(跟踪)符号链接         
3. access 函数
原型: int access(const char *pathname, int mode);
作用: 测试指定文件是否拥有某种权限
4. access 函数
原型: int chmod(const char *path, mode_t mode);
作用: 改变文件权限,mode必须是一个8进制数
5. truncate 函数
原型: int truncate(const char *path, off_t length);
作用: 将参数path指定的文件大小改变为参数length的大小;如果原来文件大小比参数length大,则超过部分删除。
6. link 函数、unlink 函数
原型: int link(const char *oldpath, const char *newpath);
作用: link为创建一个硬链接;
unlink为删除一个文件的目录项并减少它的链接数,若成功返回0,否则返回-1,错误原因存于errno;如果想通过调用这个函数成功删除文件,你就必须拥有这个文件的所属目录的写和执行权限
    使用:  ① 如果是符号链接,删除符号链接;
                ② 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode;
                ③ 3. 如果文件硬链接数为0,但进程已打开该文件,并持有该文件描述符,则等进程结束后再删除该文件。利用该特性创建临时文件,先open或create创建一个文件,马上unlink该文件。
7. rename 函数
原型: int rename(const char *oldpath, const char *newpath);
作用: 文件重命名
4. 目录操作函数
1. chdir 函数
原型:  int chdir(const char *path);
作用: 修改当前进程的工作路径
2. getcwd 函数
原型: char *getcwd(char *buf, size_t size);
作用: 获取当前进程的工作目录
3. mkdir 函数
原型: int mkdir(const char *pathname, mode_t mode);
作用: 创建目录;创建的目录需要有执行权限,否则无法进入目录。
4.rmdir 函数
原型: int rmdir(const char *pathname);
作用: 删除一个空目录。
5 .opendir 函数
原型: DIR *opendir(const char *name);
作用: 打开一个目录。
返回值:DIR结构指针,该结构是一个内部结构,保存所打开的目录信息,作用类似于FILE结构,函数出错时返回 NULL。
6. readdir 函数
原型: struct dirent *readdir(DIR *dirp);
作用: 读目录。
返回值:返回一条记录项
  1. struct dirent
  2. {
  3.         ino_t                        d_ino;                // 此目录进入点的inode
  4.         ff_t                        d_off;                // 目录文件开头至此目录进入点的位移
  5.         signed        short int         d_reclen;        //d_name的长度,不包括
  6.         unsigned char                 d_type;                // d_name所指的文件类型
  7.         har                         d_name[256];         // 文件名
  8. }
复制代码
文件类型d_type:
  1. DT_BLK                        块设备
  2. DT_CHR                字符设备
  3. DT_DIR                        目录
  4. DT_LNK                软链接
  5. DT_FIFO                管道
  6. DT_REG                普通文件
  7. DT_SOCK                套接字
  8. DT_UNKNOWN        未知
复制代码
7.closedir 函数
作用: 关闭目录。
5. VFS虚拟文件系统
        inux支持各种各样的文件系统格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不同的磁盘分区、光盘或其它存储设备都有不同的文件系统格式,然而这些文件系统都可以mount到某个目录下,使我们看到一个统一的目录树,各种文件系统上的目录和文件我们用ls命令看起来是一样的,读写操作用起来也都是一样的,这是怎么做到的呢?Linux内核在各种不同的文件系统格式之上做了一个抽象层,使得文件、目录、读写访问等概念成为抽象层的概念,因此各种文件系统看起来用起来都一样,这个抽象层称为虚拟文件系统(VFS,Virtual Filesystem)。
vfs.png

vfs2.png

dup/dup2函数
        up和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体。如果两个文件描述符指向同一个file结构体,File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引用计数是2。如果两次open同一文件得到两个文件描述符,则每个描述符对应一个不同的file结构体,可以有不同的File Status Flag和读写位置。请注意区分这两种情况。
dup/dup2实现重定向:
  1. #include <unistd.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. int main(void)
  8. {
  9.         int fd, save_fd;
  10.         char msg[] = "This is a test\n";

  11.         fd = open("somefile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
  12.         if(fd<0) {
  13.         perror("open");
  14.         exit(1);
  15.         }

  16.         save_fd = dup(STDOUT_FILENO);
  17.         dup2(fd, STDOUT_FILENO);
  18.         close(fd);

  19.         write(STDOUT_FILENO, msg, strlen(msg));
  20.         dup2(save_fd, STDOUT_FILENO);

  21.         write(STDOUT_FILENO, msg, strlen(msg));
  22.         close(save_fd);
  23.         return 0;
  24. }
复制代码
linux-sys.zh.bmp

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-8-15 10:55:38 | 显示全部楼层
那个补充:数据块寻址  那个图  没理解的了  能详细解释下吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-26 00:46

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表