kernel笔记——VFS
vfs(the virtual filesystem, virtual file switch)为应用程序访问文件提供了统一的接口,如read、write、open等。
下面我们看加载文件系统模块、格式化磁盘、挂载磁盘,这些步骤相应的内核实现,vfs在其中又发挥了哪些作用。
文件系统在内核中由file_system_type结构表示,当我们执行以下命令加载文件系统模块时:
modprobe ext2
内核使用了以下函数完成ext2模块注册:
调用module_init(init_ext2_fs),初始化模块;
在init_ext2_fs函数中,调用register_filesystem注册一个文件系统,register_filesystem将传入的ext2_fs_type加入到一个文件系统链表中,全局变量file_systems为文件系统链表的头结点。
接着我们格式化磁盘:
mkfs.ext2 /dev/sda5
以上命令将磁盘的格式设置为ext2文件系统的格式,使得磁盘分区的格式按照指定文件系统的格式定义,如设定块大小、数据块是否连续存放等。
格式化之后,进行磁盘挂载:
mount /dev/sda5 /mn
mount命令使用了mount系统调用,在中,可以看到mount系统调用的定义SYSCALL_DEFINE5(mount, …),该函数中,调用do_mount完成实质的挂载工作。
do_mount首先调用kern_path获取挂载点相关信息,kern_path调用do_path_lookup,通过一系列函数调用,挂载点信息最终被记录在nameidata类型的变量中,nameidata包含挂载点的dentry和vfsmount
接着调用do_new_mount,其用以将ext2文件系统安装到/mnt,do_new_mount调用do_kern_mount,do_kern_mount调用vfs_kern_mount,vfs_kern_mount通过调用alloc_vfsmnt新建一个vfsmount,vfs_kern_mount再调用type->get_sb,这里type为之前加载ext2模块时注册的ext2_fs_type,get_sb对应于ext2_fs_type中的ext2_get_sb
ext2_get_sb调用get_sb_bdev,get_sb_bdev中调用sget新建一个super_block,之后调用ext2_fill_super,ext2_fill_super中调用ext2_iget新建一个inode,该inode作为挂载点的虚拟根,之后调用d_alloc_root新建一个dentry
回到do_new_mount函数,do_kern_mount返回后将调用do_add_mount,do_add_mount调用graft_tree将之前新建的vfsmount添加到一个hash表中
以上是挂载的底层实现过程,过程中创建了vfsmount、super_block、inode和dentry结构类型的变量,通过结构内部指针字段将各结构与其他结构关联,将super_block、inode等结构的操作函数设置为与新文件系统对应的操作函数(如ext2_sops、ext2_dir_inode_operations),并使用新建的vfsmount、dentry、inode替换原来/mnt的相应结构。
所有挂载点,都有一个虚拟的、隐藏的根,挂载完成之后,操作挂载点中的目录或文件,将使用新注册的方法,如/dev/sda5挂载上/mnt之后,/mnt下的目录与文件将使用ext2提供的super_operations、inode_operations、file_operations等方法;而在挂载之前,/mnt使用的是”/”挂载点提供的方法。
使用crash解析vmcore文件,执行mount命令可以看到系统的挂载项:
crash> mount
VFSMOUNT SUPERBLK TYPE DEVNAME DIRNAME
ffff8806281ccd80 ffff8806281ca400 rootfs rootfs /
ffff880629f895c0 ffff880631d83400 tmpfs udev /dev
ffff880629f893c0 ffff8806319b1400 devpts devpts /dev/pts
ffff8806281cc180 ffff880633d8a400 ext3 /dev/sda2 /
ffff8806281cc680 ffff880628785800 proc proc /proc
ffff880636be9ec0 ffff8806281ca800 sysfs sysfs /sys
ffff880629f896c0 ffff880628dc2400 debugfs debugfs /sys/kernel/debug
ffff880636be95c0 ffff880633586400 binfmt_misc none /proc/sys/fs/binfmt_misc
ffff880629f894c0 ffff8806351b1400 tmpfs tmpfs /dev/shm
ffff880636be92c0 ffff88062fba5400 ext2 /dev/sda5 /mnt
每一个挂载点对应一个vfsmount结构,以上输出中最左一列就是挂载点vfsmount结构对应的地址。
再执行struct vfsmount <vfsmount< span="">的地址>,我们就可以将vfsmount结构的内容解析出来:
struct vfsmount {
……
mnt_parent = 0xffff8806281cc180,
mnt_mountpoint = 0xffff88061572ebc0,
mnt_root = 0xffff88051e2bd240,
……
mnt_devname = 0xffff880634fe2460 "/dev/sda5",
};
对于vfsmount结构,我们主要看以上几个字段:
- mnt_parent: 上一层挂载点的vfsmount结构(这里为”/”对应的vfsmount)
- mnt_mountpoint: 其为挂载点对应的dentry(这里为mnt对应的dentry)
- mnt_root: 上一层挂载点对应的dentry(这里为”/”对应的dentry)
- mnt_devname: 指示挂载设备名称(这里为/dev/sda5)
在进行挂载之前,执行stat /mnt命令可以看到,可以看到/mnt与一个inode对应:
linux # stat /mnt
File: `/mnt'
Size: Blocks: IO Block: directory
Device: 802h/2050d Inode: Links:
在挂载之后,inode变成另一个数值:
linux # stat /mnt
File: `/mnt'
Size: Blocks: IO Block: directory
Device: 805h/2053d Inode: Links:
新的inode不再是磁盘上的一个项,而存在于内存中(即以上mount步骤中建立的虚拟根),并且对应于挂载点上新的文件系统。
我们可以通过解析挂载点的vfsmount,再解析挂载点的dentry结构,即mnt_root,再解析相应的inode结构,即d_inode,就可找到挂载点相应的inode结构,查看到i_ino字段的值。
总的而言,使用mount挂载文件系统,其主要完成以下两项工作:
- 创建一个vfsmount
- 新建一个虚拟根,替换原来的inode
Reference: Chapter 13 - The Virtual Filesystem, Linux kernel development.3rd.Edition
kernel笔记——VFS的更多相关文章
- LFS:kernel panic VFS: Unable to mount root fs
说明: 使用Vm虚拟机构建自己的LFS系统时,系统引导不成功,提示 kernel panic VFS: Unable to mount root fs 参考链接:http://www.52os.net ...
- Linux内核学习笔记——VFS
概念: ①硬链接:若一个 inode 号对应多个文件名,则称这些文件为硬链接.即硬链接就是同一个文件使用了多个别名.硬链接可由命令 link 或 ln 创建. 其特性: 文件有相同的 inode 及 ...
- LINUX kernel笔记系列 :IO块参数 图
Linux下,I/O处理的层次可分为4层: 系统调用层,应用程序使用系统调用指定读写哪个文件,文件偏移是多少 文件系统层,写文件时将用户态中的buffer拷贝到内核态下,并由cache缓存该部分数 ...
- kernel笔记——内核编译与进程管理
内核与操作系统 由于一些商业操作系统设计上的缺陷以及日益庞杂,“操作系统”的概念对很多人而言变得含糊不清.在进一步讨论Linux内核的话题前,我们先区分“内核”与“操作系统”这两个概念. 操作系统:指 ...
- kernel笔记——库文件与系统调用
库文件 先从我们熟悉的c库入手,理解系统调用(system call).c代码中调用printf函数,经历了以下调用过程: 最终输出的功能由内核中write调用完成,c库封装了系统调用. 对于以下 ...
- kernel笔记——进程调度
调度器完成以下任务: 时钟中断(或类似的定时器)时间内刷新进程的时间片,设置进程调度标志 系统调用返回或中断完成时检查调度标志 schedule函数 内核代码中完成进程调度的函数为schedule() ...
- kernel笔记——中断
cpu与磁盘.网卡.键盘等外围设备(相对于cpu和内存而言)交互时,cpu下发I/O请求到这些设备后,相对cpu的处理能力而言,磁盘.网卡等设备需要较长时间完成请求处理. 那么在请求发出到处理完成这段 ...
- kernel笔记——内核同步与锁
内核同步 内核同步解决并发带来的问题,多个线程对同一数据进行修改,数据会出现不一致的情况,同步用于保护共享数据等资源. 有两种形式的并发: 同时进行式并发,在不同cpu上执行的进程同时访问共享数据 二 ...
- kernel笔记——定时器与时间管理
内核中时钟主要完成以下作用: 记录系统运行时间 完成时间相关的统计功能,如cpu占用率等 定时功能,设定某个进程一段时间后完成某项任务 为实现以上功能,硬件以及内核提供了不同类型的时钟. RTC 实时 ...
随机推荐
- Ajax上传图片以及上传之前先预览
手头上有几个小项目用到了easyUI,一开始决定使用easyUI就注定了项目整体上前后端分离,基本上所有的请求都采用Ajax来完成.在文件上传的时候用到了Ajax上传文件,以及图片在上传之前的预览效果 ...
- SQL修改表字段,加附属属性
GO Go EXEC sys.[sp_addextendedproperty] @name = 'MS_Description',@value = '是否填写表单',@level0type = 'SC ...
- leetcode — best-time-to-buy-and-sell-stock-ii
/** * Source : https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ * * * * Say you ...
- [SRM603] WinterAndSnowmen
Description Sol 设 \(A=\text{XOR}(X)\),\(B=\text{XOR}(Y)\). 因为 \(A<B\),所以写下他们的二进制表示,一定是最高的几位先是相等,紧 ...
- 【Zabbix】Zabbix-agent自动化脚本
zabbix-agent自动化脚本 作用:批量部署zabbix-agent.用于上百台虚拟机都可以被Zabbix监控. 脚本名:inst-agent.sh #!/bin/bash echo " ...
- 利用 c# linq 实现跨数据库的联合查询
有个需求就是,我们要查询的信息分布在两个不同的数据库中,通过外键相互关联起来,然后返回datatable在前端展示内容. 根据需求我们可以考虑c#的linq 先在从不同的数据中获取相关的datatab ...
- ECMASCript 2019可能会有哪些特性?
最近这些年,ECMASCript标准发展节奏非常稳定,每年都会发布新的特性.那么,ECMASCript 2019可能会有哪些特性呢? ECMASCript语法提案的批准流程 JavaScript的标准 ...
- 从.Net到Java学习第六篇——SpringBoot+mongodb&Thymeleaf&模型验证
SpringBoot系列目录 SpringBoot整合mongodb MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.如果你没用过Mong ...
- Linux tail 命令
tail 命令可用于查看文件的内容,有一个常用的参数 -f 常用于查阅正在改变的日志文件. tail -f filename 会把 filename 文件里的最尾部的内容显示在屏幕上,并且不断刷新,只 ...
- Java list.remove( )方法需要注意的地方
List<Integer> integerList = new ArrayList<>(); 当我们要移除某个Item的时候 remove(int position):移除某个 ...