转自:https://blog.csdn.net/zr_lang/article/details/39963253

一直想写有些关于文件系统的博文,但是由于近一年来实在太忙,所以没有时间写。前几日赶上放国庆节假期,回来后又正好处在RHEL-6.6刚发布,RHEL-7.1还没有开始繁重工作的阶段,所以抽出一些时间准备写一些东西。不知道是否能坚持长久,可能在忙起来后会拖后进度吧。反正也不着急,慢慢写。

既然要分析就要选择一个确定的linux版本进行说明,目前最新的linux稳定版是3.16.3,就选这个内核了。之后的文章都以这个版本为基础。

研究linux
fs的入手点应该是哪呢?可能仁者见仁,智者见智。我一直以来也都是零零散散的翻阅着有关super block, inode, dentry,
file等等内容的各种代码和资料,但是知识始终是散乱的,无法拼凑成完整的体系。现在我要试图整理顺一个整套的文件系统知识,我决定从file_system_type入手。

本文是要尽力将我已经掌握的分块的知识串连起来,所以至少了解linux vfs的几个结构是必须的,super_block, inode, dentry, file等。

在include/linux/fs.h里定义着几个基本数据结构——block_device,
file_system_type,
inode和file。其中block_device一般和块设备操作,如硬盘、分区等有关,文件系统的操作多半都会反映到硬盘等块设备上的操作,如读取super

block、读写数据等,所以block_device掌管着文件系统的底层设备。由于我们从file_system_type开始研究,所以这里先不管block_device。

来看file_system_type的结构:

  1. struct file_system_type {
  2. const char *name;
  3. int fs_flags;
  4. #define FS_REQUIRES_DEV         1
  5. #define FS_BINARY_MOUNTDATA     2
  6. #define FS_HAS_SUBTYPE          4
  7. #define FS_USERNS_MOUNT         8       /* Can be mounted by userns root */
  8. #define FS_USERNS_DEV_MOUNT     16 /* A userns mount does not imply MNT_NODEV */
  9. #define FS_RENAME_DOES_D_MOVE   32768   /* FS will handle d_move() during rename() internally. */
  10. struct dentry *(*mount) (struct file_system_type *, int, const char *, void *);
  11. void (*kill_sb) (struct super_block *);
  12. struct module *owner;
  13. struct file_system_type * next;
  14. struct hlist_head fs_supers;
  15. // ....
  16. //为了说明方便,此处省略若干锁相关变量。
  17. //....
  18. }

name: 文件系统的名字,如xfs, ext2等

fs_flags: 说明文件系统的类型,下面的宏定义代表了它的几种类型:

FS_REQUIRES_DEV: 文件系统必须在物理设备上。

FS_BINARY_MOUNTDATA: mount此文件系统时(参见mount_fs函数 -
fs/super.c)需要使用二进制数据结构的mount data(如每个位域都有固定的位置和意义),常见的nfs使用这种mount
data(参见struct nfs_mount_data结构 - include/uapi/linux/nfs_mount.h)。

FS_HAS_SUBTYPE: 文件系统含有子类型,最常见的就是FUSE,FUSE本是不是真正的文件系统,所以要通过子文件系统类型来区别通过FUSE接口实现的不同文件系统。

FS_USERNS_MOUNT: 文件系统每次挂载都后都是不同的user namespace,如用于devpts。

FS_USERNS_DEV_MOUNT: user namespace挂载支持MNT_DEV, 即非nodev模式。

FS_RENAME_DOES_D_MOVE: 文件系统将把重命名操作reame()直接按照移动操作d_move()来处理,主要用于网络万件系统。

mount: 代替早期的get_sb(),用户挂载此文件系统时使用的回调函数。

kill_sb: 删除内存中的super block,在卸载文件系统时使用。

owner: 指向实现这个文件系统的模块,通常为THIS_MODULE宏。

next: 指向文件系统类型链表的下一个文件系统类型。

fs_supers: 具有同样此文件系统类型的超级块结构,都串连在这个表头下。

有了file_system_type,现在就是如何使用file_systems_type了。file_system_type的基本操作都在fs/filesystems.c文件里,文件不长,可以简短的浏览一下都有那些操作函数。最主要的变量莫过于此文件内的全局变量:

// 此变量是文件系统类型单链表的头指针

static struct file_system_type *file_systems;

而其中最主要的函数莫过于:

  1. int register_filesystem(struct file_system_type * fs)
  2. {
  3. int res = 0;
  4. struct file_system_type ** p;
  5. BUG_ON(strchr(fs->name, '.'));
  6. if (fs->next)
  7. return -EBUSY;
  8. write_lock(&file_systems_lock);
  9. // 遍历全局file_systems链表,尝试查找本次要注册文件系统名。
  10. p = find_filesystem(fs->name, strlen(fs->name));
  11. if (*p) // 如果不为NULL,则说明找到了重名的文件系统。注册失败。
  12. res = -EBUSY;
  13. else // 如果返回NULL,说明已经到链表结尾,可以注册此文件系统。
  14. *p = fs; // 将此文件系统链接到链表结尾。
  15. write_unlock(&file_systems_lock);
  16. return res;
  17. }
当然与注册相对应的就是注销,
unregister_filesystem就是register的反操作。除了注册文件系统操作以外还有一个相对重要的函数get_fs_type,接受文件系统名称(如xfs)作为参数,在*file_systems链表里查找到对应的file_system_type成员。
mount/get_sb和kill_sb方法
我觉得file_system_type,或者说register文件系统时就是要完成两个主要的事情:
1. 告诉kernel自己叫什么(一个唯一的name)
2. 告诉kernel怎么使用自己的超级块信息,以便完成挂载自己的操作(过去的get_sb现在的mount)。
所以mount和kill_sb回调函数就是file_system_type中最需要重点实现的了,因为这里涉及到每个文件系统的具体实现,所以我们就不详细的一一说明,只说一下大概的实现死路。fs/下实现着不同的文件系统,每一个目录名基本上就代表一个具体文件系统的实现,以xfs为例,fs/xfs/下就是xfs文件系统的实现。而file_system_type结构一般被定义在super.c里(一般,不是所有)。那么看一下fs/xfs/xfs_super.c里如何定义xfs的file_system_type:

  1. static struct file_system_type xfs_fs_type = {
  2. .owner                  = THIS_MODULE,
  3. .name                   = "xfs",
  4. .mount                  = xfs_fs_mount,
  5. .kill_sb                = kill_block_super,
  6. .fs_flags               = FS_REQUIRES_DEV,
  7. }

owner说明xfs模块本身拥有这个file_system_type。

name是xfs。

fs_flags是FS_REQUIRES_DEV,表明xfs一定要被使用在物理设备上。

接下来mount=xfs_fs_mount,说明 xfs_fs_mount这个函数实现了xfs的具体mount操作。搜索xfs_fs_mount,在本文件里看到了它的实现:

  1. STATIC struct dentry *
  2. xfs_fs_mount(
  3. struct file_system_type *fs_type,
  4. int                     flags,
  5. const char              *dev_name,
  6. void                    *data)
  7. {
  8. return mount_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super);
  9. }

看起来就是一个mount_bdev()函数,但是看参数可以看到一些重要的东西

fs_type不用多说,携带file_system_type的信息,这里传递它主要是因为它携带了super block的链表和很多锁变量。

flags文件系统的通用挂载选项。

dev_name是mount操作时的设备名,如/dev/sda1。后面会用到这个设备名找到对应的设备信息,从而从中获得super block。

data是挂载时指定的挂载选项信息。

xfs_fs_fill_super是一个由xfs特定实现的fill_super方法,用来根据xfs文件系统的特性解析mount data并继续填充super block的字段,并且初始化挂载点的根索引节点对象和目录项对象。

对于mount的具体过程将在后续内容中详细介绍。

总结:

为什么我认为file_system_type是学习linux
fs的开始,因为注册文件系统是让一个具体文件系统能够生效的第一步,而注册文件系统实际就是注册了一个file_system_type实例。如xfs文件系统,加载xfs模块是使用xfs的必要条件,加载xfs模块时就会将xfs的file_system_type注册到kernel中,当然xfs要先具体化一个自己的file_system_type,然后注册。

当一个文件系统注册进内核后,它并没有被使用,只有mount这个文件系统后,这个文件系统才真正工作,我们一般这样初始化并使用一个文件系统:

mkfs.xfs /dev/sda1

mount -t xfs /dev/sda1 /mnt

mkfs是用户态工具,根据每个文件系统的不同实现有不同的mkfs方法。对于硬盘来说,mkfs就是向硬盘中按照某个文件系统的格式写入文件系统的信息到硬盘中,如文件系统类型,block
size,bitmap,
inode索引等,这些内容主要组成了一个文件系统的超级块,本来这些信息是很难理解的,单是对于文件系统本身来说他们就是一个个有效的数据。
在mount文件系统时,这个文件系统注册的mount回调函数(过去的get_sb函数)就有用了,它由具体文件系统实现,可以从设备上(如磁盘)读取文件系统的超级块信息,用以完整整个mount操作。(关于mount操作我们后面叙述)

所以说file_system_type主要就是完成两件事,一个是告诉系统自己叫什么,一个是告诉系统怎样解析自己特有的数据结构完成mount操作。

http://blog.csdn.net/zr_lang/article/details/40325241 (mount 七)

http://blog.csdn.net/zr_lang/article/details/40343899 (mount 六)

http://blog.csdn.net/zr_lang/article/details/40115013 (mount 五)

http://blog.csdn.net/zr_lang/article/details/40080979 (mount 四)

http://blog.csdn.net/zr_lang/article/details/40049305 (mount 三)

http://blog.csdn.net/zr_lang/article/details/40002285 (mount 二)

http://blog.csdn.net/zr_lang/article/details/39963253 (mount 一)

mount过程分析之一(基于3.16.3内核)【转】的更多相关文章

  1. Linux系统启动那些事—基于Linux 3.10内核【转】

    转自:https://blog.csdn.net/shichaog/article/details/40218763 Linux系统启动那些事—基于Linux 3.10内核 csdn 我的空间的下载地 ...

  2. [转]Linux芯片级移植与底层驱动(基于3.7.4内核)

      1.   SoC Linux底层驱动的组成和现状 为了让Linux在一个全新的ARM SoC上运行,需要提供大量的底层支撑,如定时器节拍.中断控制器.SMP启动.CPU hotplug以及底层的G ...

  3. 基于x86架构的内核Demo的详细开发文档

    http://hurlex.0xffffff.org/ 这里是hurlex这个基于x86架构的内核Demo的详细开发文档, 包含PDF文档和生成PDF的XeLaTex源码和文档每章节的阶段代码. 你可 ...

  4. 做自己的docker镜像(基于ubuntu:16.04)

    基于ubuntu:16.04 apt-get update -y apt-get install sudo -y 换源 sudo apt-get install vim sudo vim /etc/a ...

  5. 基于Linux-3.9.4内核的GDB跟踪系统调用实验

    382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验环境 win10 -> VMware -> Ubuntu1 ...

  6. 基于tiny4412的Linux内核移植 -- eMMC驱动移植(六)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  7. 基于tiny4412的Linux内核移植 -- DM9621NP网卡驱动移植(四)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  8. 基于tiny4412的Linux内核移植(支持device tree)(二)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  9. Linux mount/unmount命令(6/16)

    linux是一个优秀的开放源码的操作系统,可以运行在大到巨型小到掌上型各类计算机系统上,随着 linux系统的日渐成熟和稳定以及它开放源代码特有的优越性,linux在全世界得到了越来越广泛的应用.现在 ...

随机推荐

  1. 第十一周(11.24-12.01)----WBS功能分解

    功能 子功能 二级子功能 预计花费时间(小时) 游戏基础功能 显示首界面 绘制产产品主logo及不同难度下的布局 4   游戏 难度选择(初级.中级.高级) 4     退出整个程序 1     放弃 ...

  2. [Java]Object有哪些公用方法?

    1.clone方法 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常. 主要是JAVA里除了8种基本类型传 ...

  3. Windows 下 Docker 的简单学习使用过程之三 创建images 导出images

    1. 创建images 主要有两种方法, 一种是docker commit 一种是docker build 其中有一个很明显的区别: docker commit 是将运行状态的虚拟机 进行 生成ima ...

  4. [代码]--GridControl使用技巧总结,更新中...

    1如何禁用GridControl中单击列弹出右键菜单 设置Run Design->OptionsMenu->EnableColumnMenu 设置为:false 2如何定位到第一条数据/记 ...

  5. oracle 查出一个表中字段值出现次数大于2的所有记录

    表web_order  列 name ,businesscode, a.account 周桥 18929609222 3754031157710000妙药 18929609233 3754031157 ...

  6. CJB的大作

    Description 给你一个长度不超过100的字符串.一共进行\(N\)次操作,第\(i\)次操作是将当前字符串复制一份接到后面,并将新的一份循环移位\(k_i\)(\(1 \le k_i \le ...

  7. JAVA AES CBC PKCS5Padding加解密

    package com.hzxc.groupactivity.util; /** * Created by hdwang on 2019/1/17. */ import org.slf4j.Logge ...

  8. bzoj 2275: [Coci2010]HRPA

    据说叫斐波那契博弈. 先手最少取的石子数是把n用斐波那契数列拆分后最小的数. 原题+证明: http://blog.csdn.net/acm_cxlove/article/details/783501 ...

  9. 51nod 1450 闯关游戏

    首先肯定要先把所有的关卡打通后去找两星几率最大的关卡刷星(论打游戏经验的重要性). 所以从两星几率小的关打起,记录当前拿到x个星星的几率和当前走过的期望步数,如果发现剩下的关必须全两星,就直接计算答案 ...

  10. 关于NIO一些优化

    1. 使用NIO开发web服务,传输文件内容,可以使用FileChannel.transferTo(position,count,socketChannel)来提升性能: 经过测试,确实能提升10% ...