注:本文分析基于linux-4.18.0-193.14.2.el8_2内核版本,即CentOS 8.2

1 super_block

super_block,即超级块代表的是一种文件系统类型,比如ext3、ext4都有对应的super_block结构体。一台机器可以有多块硬盘,一个硬盘可以有多个分区,每个分区都有自己的文件系统类型,可以是一样的,也可以不一样。我们来了解下超级块的主要成员变量。

2 super_block主要成员变量

struct super_block {struct list_head s_list; //通过该变量链接到超级块全局链表super_blocks上dev_t         s_dev;      //该文件系统对应的块设备标识符unsigned char       s_blocksize_bits;unsigned long      s_blocksize; //该文件系统的block sizeloff_t           s_maxbytes; //文件系统支持的最大文件struct file_system_type    *s_type; //文件系统类型,比如ext3、ext4const struct super_operations   *s_op; //超级块的操作函数const struct dquot_operations  *dq_op; //文件系统限额相关操作const struct quotactl_ops   *s_qcop; //磁盘限额const struct export_operations *s_export_op;unsigned long        s_flags; //文件系统的mount标记unsigned long        s_iflags;   /* internal SB_I_* flags */unsigned long        s_magic;  //该文件系统类型的魔术字struct dentry        *s_root; //全局根目录的dentry项...struct block_device  *s_bdev;  //对应的块设备struct backing_dev_info *s_bdi; //超级块对应的BDI设备struct mtd_info      *s_mtd;//通过该变量,链接到file_system_type中的fs_supers链表struct hlist_node s_instances;...char         s_id[32];   /* Informational name */uuid_t          s_uuid;     /* UUID tune2fs -l可以查看*/void            *s_fs_info; //指向具体文件系统超级块结构,如ext4_sb_info...const struct dentry_operations *s_d_op; //该超级块默认的目录项操作函数...struct shrinker s_shrink; //每个超级块注册的shrink函数,用于内存回收.../* AIO completions deferred from interrupt context */struct workqueue_struct *s_dio_done_wq;...//该超级块对应的未在使用dentry列表struct list_lru      s_dentry_lru ____cacheline_aligned_in_smp;//该超级块对应的未在使用inode列表struct list_lru       s_inode_lru ____cacheline_aligned_in_smp;.../* s_inode_list_lock protects s_inodes */spinlock_t     s_inode_list_lock ____cacheline_aligned_in_smp;struct list_head s_inodes;   //该超级块包含的所有inodespinlock_t      s_inode_wblist_lock;struct list_head    s_inodes_wb;    //该超级块正在回写的inode
};

而对应的文件系统则由file_system_type结构描述,

struct file_system_type {const char *name; //文件系统名称,如ext4,xfs...struct dentry *(*mount) (struct file_system_type *, int,const char *, void *); //对应的mount函数void (*kill_sb) (struct super_block *);struct module *owner;struct file_system_type * next; //通过该变量将系统上所有文件系统类型链接到一起struct hlist_head fs_supers; //该文件系统类型锁包含的超级块对象...
};

3 注册一个文件系统

内核为了管理系统上的所有文件系统,创建了一个全局链表file_systems,系统上所有文件系统类型都会挂载在上面,我们可以通过/proc/filesystems查看系统所有的文件系统类型。

下面我们以ext4文件类型,大概了解下注册过程。

static struct file_system_type ext4_fs_type = {.owner       = THIS_MODULE,.name        = "ext4",.mount      = ext4_mount,.kill_sb  = kill_block_super,.fs_flags   = FS_REQUIRES_DEV,
};int register_filesystem(struct file_system_type * fs)
{int res = 0;struct file_system_type ** p;BUG_ON(strchr(fs->name, '.'));if (fs->next)return -EBUSY;write_lock(&file_systems_lock);//在已注册的全局文件系统链表file_systems中查找,看是否已经注册过p = find_filesystem(fs->name, strlen(fs->name));if (*p)//如果查找到就返回错误,同一个文件系统不能注册两次res = -EBUSY;else//没查找到,直接将该文件系统添加链表末尾*p = fs;write_unlock(&file_systems_lock);return res;
}static struct file_system_type **find_filesystem(const char *name, unsigned len)
{struct file_system_type **p;//在已注册的全局文件系统链表file_systems中查找for (p = &file_systems; *p; p = &(*p)->next)if (strncmp((*p)->name, name, len) == 0 &&!(*p)->name[len])break;return p;
}

可见,同个类型的文件系统只能注册一次,因此全局文件系统链表file_systems中不可能出现相同的文件系统类型

4 创建一个超级块

我们再以ext4文件系统为例,看下超级块创建的过程。这一些要从文件系统的挂载开始说起,

static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,const char *dev_name, void *data)
{return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
}struct dentry *mount_bdev(struct file_system_type *fs_type,int flags, const char *dev_name, void *data,int (*fill_super)(struct super_block *, void *, int))
{...//分配新的超级块s = sget(fs_type, test_bdev_super, set_bdev_super, flags | SB_NOSEC,bdev);if (s->s_root) {...} else {s->s_mode = mode;snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);sb_set_blocksize(s, block_size(bdev));//对新的超级块进行信息的填充,对于ext4,就是调用ext4_fill_supererror = fill_super(s, data, flags & SB_SILENT ? 1 : 0);...}...
}

主要就是两点:

  • 分配超级块
  • 填充超级块

我们先看分配超级块,有几点需要考虑:

  • 该设备是否已经mount过,不能重复mount,就像同个类型的文件系统也不能重复注册
  • 分配超级块后需要做一些初始化工作,比如各种链表初始化,以及设置缓存回收函数等
  • 分配完后就要考虑如何管理这个超级块,要加入全局超级块链表,对应的文件类型也要加到全局文件系统列表,同时还要把刚才初始化设置的缓存回收函数注册到全局回收链表
struct super_block *sget(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),int flags,void *data)
{...return sget_userns(type, test, set, flags, user_ns, data);
}           //sget_userns - find or create a superblock
struct super_block *sget_userns(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),int flags, struct user_namespace *user_ns,void *data)
{struct super_block *s = NULL;struct super_block *old;int err;...//先检查对应文件系统上的fs_supers链表,查看该设备是否已经mount过//有mount过,直接返回旧的超级块if (test) {hlist_for_each_entry(old, &type->fs_supers, s_instances) {//通过传入的test_bdev_super判断挂载的超级块对应的设备是否相同if (!test(old, data))continue;...return old;}}//没mount过,创建新的超级块if (!s) {spin_unlock(&sb_lock);//创建并初始化超级块s = alloc_super(type, (flags & ~SB_SUBMOUNT), user_ns);if (!s)return ERR_PTR(-ENOMEM);goto retry;}...//把对应文件系统类型赋值到超级块上s->s_type = type;strlcpy(s->s_id, type->name, sizeof(s->s_id));//将新的超级块通过s_list挂到全局super_blocks链表上list_add_tail(&s->s_list, &super_blocks);//把该超级块的文件系统实例挂到对应文件系统的列表头//比如有两个分区都是ext4文件系统,那fs_supers上就会挂上两个超级块信息hlist_add_head(&s->s_instances, &type->fs_supers);spin_unlock(&sb_lock);get_filesystem(type);//把该超级块的shrink函数挂到全局shrink链表shrinker_list//这样系统回收内存时,只要调用shrinker_list的shrink函数就可以了register_shrinker_prepared(&s->s_shrink);return s;
}static struct super_block *alloc_super(struct file_system_type *type, int flags,struct user_namespace *user_ns)
{...//初始化各个链表和锁INIT_HLIST_NODE(&s->s_instances);INIT_HLIST_BL_HEAD(&s->s_roots);mutex_init(&s->s_sync_lock);INIT_LIST_HEAD(&s->s_inodes);spin_lock_init(&s->s_inode_list_lock);INIT_LIST_HEAD(&s->s_inodes_wb);spin_lock_init(&s->s_inode_wblist_lock);...s->s_shrink.seeks = DEFAULT_SEEKS;//超级块缓存回收函数s->s_shrink.scan_objects = super_cache_scan;s->s_shrink.count_objects = super_cache_count;s->s_shrink.batch = 1024;s->s_shrink.flags = SHRINKER_NUMA_AWARE | SHRINKER_MEMCG_AWARE;if (prealloc_shrinker(&s->s_shrink))goto fail;//初始化存放未使用的dentry链表s_dentry_lruif (list_lru_init_memcg(&s->s_dentry_lru, &s->s_shrink))goto fail;//初始化存放未使用的indode链表s_inode_lruif (list_lru_init_memcg(&s->s_inode_lru, &s->s_shrink))goto fail;return s;fail:destroy_unused_super(s);return NULL;
}

回过头,我们再看下超级块的填充,虽然初始化时设置了一些,但是还是有很多和具体文件系统相关的信息没有体现,

  • 针对具体的文件系统,设置对应的超级块信息,super_block是VFS层的体现,具体到实际文件系统,还有对应的超级块
  • 设置该文件系统对应的超级块操作函数,里面包含了对inode资源本身的一些操作集合,比如分配,销毁,写,脏化等操作
  • 调用刚才设置的ext4_alloc_inode函数,为超级块的root分配对应的inode,同样inode也是VFS层的体现,实际ext4分配的是ext4_inode_info,而inode内嵌在该结构的vfs_inode变量中,这样就把VFS和实际文件系统关联在一起
  • 同样需要为超级块分配一个目录项,并关联到对应的inode
static const struct super_operations ext4_sops = {.alloc_inode  = ext4_alloc_inode,.destroy_inode  = ext4_destroy_inode,.write_inode  = ext4_write_inode,.dirty_inode    = ext4_dirty_inode,...
};static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);...sb->s_fs_info = sbi; //设置实际文件系统的超级块信息sbi->s_sb = sb;...sb->s_op = &ext4_sops; //设置该超级块对应的操作函数,主要是对inode自身的操作...//调用ext4_alloc_inode分配一个用于root的inoderoot = ext4_iget(sb, EXT4_ROOT_INO, EXT4_IGET_SPECIAL);...sb->s_root = d_make_root(root); //为root分配目录项...
}

5 结构关系

我们用图对以上几个结构进行展示,

文件系统之super_block相关推荐

  1. linux VFS 虚拟文件系统 简介 super_block inode dentry file

    1.简介 1.Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文 ...

  2. ext4文件系统中super_block、ext4_super_block、ext4_sb_info

    超级块代表了整个文件系统,超级块是文件系统的控制块,有整个文件系统信息,一个文件系统所有的inode都要连接到超级块上,可以说,一个超级块就代表了一个文件系统. 各种文件系统都必须实现超级快对象,该对 ...

  3. 话说文件系统——aufs源码分析(三)【转】

    本文转载自:http://www.cnblogs.com/xie0812/p/8848185.html 1. linux中设备驱动的入口都是:module_init(xxx_init);里面注册的函数 ...

  4. Linux操作系统原理与应用08:文件系统

    目录 1. Linux文件系统基础 1.1 文件系统概念 1.2 Linux文件结构 1.3 一切皆文件 1.4 文件系统类型 1.5 文件系统挂载 2. 虚拟文件系统引入 2.1 虚拟文件系统框架 ...

  5. 文件系统(ext2)

    原贴:http://man.lupaworld.com/content/develop/joyfire/kernel/7.html 文件系统 目 录 文件系统 源码导读 CD_ROM 文件页缓冲结构 ...

  6. 深入理解Linux文件系统之文件系统挂载(下)

    接着: 深入理解Linux文件系统之文件系统挂载(上) 本文为文件系统挂载专题文章的第二篇,主要介绍如何通过挂载实例关联挂载点和超级块并添加到全局文件系统树. 4. 添加到全局文件系统树 4.1 do ...

  7. Linux设备模型——设备驱动模型和sysfs文件系统解读笔记

    Linux设备模型--设备驱动模型和sysfs文件系统解读笔记 原文:https://blog.csdn.net/yj4231/article/details/7799245 将对Linux系统中的s ...

  8. 深入理解Linux文件系统之VFS

    1. VFS介绍 "一切皆文件",文件系统是linux系统的基础,Linux内核通过虚拟文件系统(Virtual File System, VFS)管理文件系统 ,VFS为所有的文 ...

  9. linux内核--设备驱动程序(学习笔记)

    字符设备驱动 一个字符设备要能够工作,需要三部分配合: 有一个设备驱动程序的ko模块,包含中断处理函数.设备操作函数.模块初始化时,将设备号注册到内核的全局数据结构cdev_map中. /dev目录下 ...

最新文章

  1. 当RabbitMQ使用Publish发布消息出现数据格式问题的解决方法
  2. keta-custom DWZ validationEng IE 下表单重复提交BUG原因及修复方法
  3. 阿里开源那个牛哄哄问题排查工具竟然不会用?最佳实践来了!
  4. php gd库画线,[PHP] GD库(十)绘制线段与圆弧 imageline、imagesetstyle 与 imagearc 函数...
  5. linux rsync 安装教程,linux下的rsync配置和使用教程
  6. 烂泥:高负载均衡学习haproxy之安装与配置
  7. 一休自评应聘:我是如何进入51CTO的?
  8. Win7家庭版启用Administrator账户
  9. AcWing 240. 食物链
  10. Leetcode-105-从前序与中序遍历序列构造二叉树
  11. iPhone 6/6 Plus 出现后,如何改进工作流以实现一份设计稿支持多个尺寸?
  12. 人工智能 一种现代方法 第1章 绪论
  13. VS2010 正式版 破解方法详解 1
  14. OpenCms登录添加验证码功能
  15. java中int与byte相互转换
  16. 好玩好用的网站,不能错过!
  17. 与卿共赴鸿蒙是什么意思,『为君倾笑°‖笑红颜』「古风」唯美的古风句子,古风签必备!...
  18. C语言学习中遇到的问题和解决方法
  19. 保研面试常考题总结(自用 不断更新)
  20. 猫云bootcdn.cn

热门文章

  1. excel实用技巧:如何构建多级下拉菜单
  2. argparse简化版图片教程
  3. 轻松学习JavaScript十一:JavaScript基本类型(包含类型转换)和引用类型
  4. 某计算机无法访问域名,在某台主机上无法访问域名为www.bbb.cn的网站,而局域网中的其他主机可..._考试资料网...
  5. 又一家安防公司即将登录创业板-睿联Reolink
  6. stata 入门(双重差分模型)
  7. HK01BS单通道电容式触控芯片IC内置稳压LDO低压复位模块
  8. Vmware Fusion中无法更改网络适配器选项
  9. android使用工具性能优化
  10. 1、会计的概念、职能和目标