一、目的

linux把文件分为常规文件、目录文件、软链接文件、硬链接文件、特殊文件(设备文件、管道文件、socket文件等)几种类型,分别对应不同的新建函数sys_open()、sys_mkdir()、sys_symlink()、sys_link()、sys_mknod()。

系统初始化阶段成功加载initrd后,调用这些接口函数创建各种文件,因此这些函数在linux文件系统初始化过程中起到了重要作用,本文将详细描述这些接口函数的实现过程。

这些接口函数主要在fs/namei.c、fs/open.c文件中实现,可以在这两个文件中找到对应的源代码。

二、快速路径查找

在以上系统调用的实现中,需要解决一个关键问题:如何根据待新建文件的路径名,快速找到父目录的位置。得到父目录的位置后,才能创建待新建文件的目录,以及分配inode节点。

例如:新建/tmp/test.txt文件,首先需要根据”/tmp/test.txt”路径名查找到test.txt父目录tmp在VFS中的位置,然后基于tmp的位置才能为test.txt文件新建目录及分配inode节点。

由于快速路径查找使用比较频繁,所以对查找效率要求较高,否则会影响系统性能。do_path_lookup(intdfs, const char *name, unsigned int flags, struct nameidata*nd)函数负责实现该功能,其中name参数是路径名,nd参数是structnameidata结构体,nd的path成员记录了父目录的位置,last成员记录了待新建文件的文件名。例如:path记录了tmp在VFS中的位置,last记录”test.txt”文件名。

该函数的实现比较复杂,不方便对照源代码讲述,因此下面重点描述了该函数的主要操作。但是为了读者方便阅读源代码,给出如下关键函数的调用关系:do_path_lookup()->path_lookupat()->path_init()->link_path_walk()->walk_component()->lookup_fast()->__d_look_rcu()->__follow_mount_rcu()

do_path_lookup()在实现过程中,按照以下三种情形来操作:

1、路径名在单文件系统中:从根目录或当前目录开始,根据目录拓扑结构,递归查找父目录;

2、路径名在多文件系统中:在递归查找时,需要从当前文件系统的挂载点切换到最终文件系统的目录拓扑结构中,然后继续查找;

3、路径名在挂载点重复挂载多文件系统的情况:在递归查找时,需要从当前文件系统的挂载点切换到最终文件系统的目录拓扑结构中,然后继续查找;

为了理解以上三种情形的具体差异,举例说明:待新建文件路径名为”/usr/tmp/log/new.txt”。

情形1:

如下图所示,路径名只在单个文件系统ext3中存在,所以根据目录拓扑结构,递归查找父目录即可;查找完成后,使用nd.path记录父目录log的位置,nd.last记录了字符串常量”new.txt”。

情形2:

在情形2中,路径名横跨了两个文件系统,minix挂载在ext3的tmp目录上;当成功挂载minix后,设置挂载点tmp为已挂载状态,并且将tmp指向minix文件系统。

路径查找程序根据路径名”/usr/tmp/log/new.txt”查找到tmp目录时,发现tmp为已挂载状态,所以从挂载点tmp切换到minix根目录;然后,确认minix根目录不是已挂载状态后,在minix文件系统中继续查找剩下的路径名”log/new.txt”;查找完成后,使用nd记录查找结果。

情形3:

在情形3中,ext3的tmp目录重复挂载了两个文件系统minix和nfs;当成功挂载minix后,设置挂载点tmp为已挂载状态;当在同一挂载点tmp挂载nfs时,发现tmp已经是挂载状态,所以从挂载点tmp切换到minix根目录,在该根目录挂载nfs文件系统,并且将minix根目录设置为已挂载状态。linux支持在同一挂载点挂载多个文件系统的操作,但是只有最后被挂载的文件系统才是可见的,所以使用ls命令只能看到nfs文件系统的内容。

路径查找程序根据路径名”/usr/tmp/log/new.txt”查找到tmp目录时,发现tmp为已挂载状态,所以从挂载点tmp切换到minix根目录;然后发现minix根目录也是已挂载状态,所以继续切换到nfs根目录;最后,确认nfs根目录不是已挂载状态后,在nfs文件系统中继续查找剩下的路径名”log/new.txt”;查找完成后,使用nd记录查找结果。

在以上操作中,由__follow_mount_rcu()函数负责目录挂载状态检测和切换操作。

三、新建文件系统调用

do_path_lookup()函数返回的structnameidata *nd数据结构,记录了待新建文件父目录的位置,所以根据nd记录的信息,在VFS树中新建文件就变得相对简单了。

由于文件系统的操作比较复杂,因此不对源代码进行详解,主要介绍系统调用的主要功能(图中绿色部分),但是给出了关键函数调用路径,便于读者查阅细节内容。

3.1、新建常规文件系统调用sys_open()

注:这里重点介绍sys_open()的新建功能,忽略打开功能(打开功能比较复杂也与主题不符),所以读者不能片面认为sys_open()的功能只是新建文件。

关键函数调用路径如下:

主要功能总结:

1)get_unused_fd_flags()新建文件描述符;

2)do_filp_open()创建常规文件的file结构体、目录项、inode节点,并将三者关联起来;

2.1)get_empty_filp()创建常规文件的file结构体;

2.2)lookup_open()调用lookup_dcache()创建常规文件的目录项目;

2.3)lookup_open()调用vfs_create()创建常规文件的inode节点;

3)fd_install()将文件描述符指向file结构体。

具体操作流程如下图所示:

3.2、新建硬链接文件系统调用sys_link()

关键函数调用路径如下:

主要功能总结:

1)user_path_at()返回硬链接目标文件的目录项位置;

2)user_path_create()创建硬链接文件的目录项;

2.1)do_path_lookup()返回硬链接文件的父目录位置

2.2)lookup_hash()根据父目录位置,创建硬链接文件的目录项;

3)vfs_link()将硬链接文件指向硬链接目标文件的inode节点。

具体操作流程如下图所示:

3.3、新建目录文件系统调用sys_mkdir()

关键函数调用路径如下:

主要功能总结:

1)user_path_create()创建目录文件的目录项

1.1)do_path_lookup()返回目录文件的父目录位置;

1.2)lookup_hash()根据父目录位置,创建目录文件的目录项;

2)vfs_mkdir()创建目录文件的inode节点;

具体操作流程如下图所示:

3.4、新建软链接文件系统调用sys_symlink()

关键函数调用路径:

主要功能总结:

1)user_path_create()创建软链接文件的父目录位置;

1.1)do_path_lookup()返回软链接文件的父目录位置;

1.2)lookup_hash()根据父目录位置,创建软链接文件的目录项;

2)vfs_symlink()创建软链接文件的inode节点,并且inode节点记录了软链接目标文件的位置;简单来说,软链接文件的内容就是软链接目标文件的路径。

具体操作流程如下图所示:

3.5、新建特殊文件系统调用sys_mknod()

关键函数调用路径:

主要功能总结:

1)user_path_create()创建特殊文件的父目录位置;

1.1)do_path_lookup()返回特殊文件的父目录位置;

1.2)lookup_hash()根据父目录位置,创建特殊文件的目录项;

2)vfs_mknod()创建特殊文件的inode节点,初始化inode的i_rdev成员,并且将i_fop成员指向默认文件操作;

注:当用户使用sys_open()打开特殊文件时,会调用默认文件操作的open成员,把设备文件挂接到inode的设备链表中,并且将i_fop成员重新指向该设备的文件操作(即设备驱动)。

具体操作流程如下图所示:

通过以上介绍,可以看出快速路径查找函数do_path_lookup()在新建文件系统调用中起到了基础性的作用,因此有必要掌握该函数的用法。

四、VFS全景图

到目前为止,initrd的加载过程就全部结束了;最后,为了更清晰的理解VFS此时的全貌,给出如下VFS全景图。

从图中看出以下特点:

1、sysfs文件系统目前还没有挂载到rootfs的某个挂载点上,后续init程序会把sysfs挂载到rootfs的sys挂载点上;

2、系统打开了/dev/console设备,说明系统已经可以使用该设备,打印信息也可以正常输出;

3、rootfs文件系统的根目录下已经准备好了init程序,内核后续会启动该程序完成剩下的初始化操作。

五、总结

目前为止,linux成功加载了initrd文件后,在内核中构建了一个基于内存的文件系统rootfs;VFS再也不是只有根目录、结构简单的小树苗了;现在,它已经成长为拥有目录、常规文件、链接文件、设备文件等多种文件类型、结构复杂的参天大树了。

linux文件系统初始化过程(5)---加载initrd(下)的更多相关文章

  1. linux文件系统初始化过程(3)---加载initrd(上)

    一.目的 本文主要讲述linux3.10文件系统初始化过程的第二阶段:加载initrd. initrd是一个临时文件系统,由bootload负责加载到内存中,里面包含了基本的可执行程序和驱动程序.在l ...

  2. linux文件系统初始化过程(4)---加载initrd(中)

    一.目的 上文详细介绍了CPIO格式的initrd文件,本文从源代码角度分析加载并解析initrd文件的过程. initrd文件和linux内核一般存储在磁盘空间中,在系统启动阶段由bootload负 ...

  3. linux文件系统初始化过程(1)---概述

    术语表: struct task:进程 struct mnt_namespace:命名空间 struct mount:挂载点 struct vfsmount:挂载项 struct file:文件 st ...

  4. linux文件系统初始化过程(6)---执行init程序

    一.目的 内核加载完initrd文件后,为挂载磁盘文件系统做好了必要的准备工作,包括挂载了sysfs.proc文件系统,加载了磁盘驱动程序驱动程序等.接下来,内核跳转到用户空间的init程序,由ini ...

  5. linux文件系统初始化过程(2)---挂载rootfs文件系统

    一.目的 本文主要讲述linux3.10文件系统初始化过程的第一阶段:挂载rootfs文件系统. rootfs是基于内存的文件系统,所有操作都在内存中完成:也没有实际的存储设备,所以不需要设备驱动程序 ...

  6. linux文件系统 - 初始化(二)

    加载initrd(上) 一.目的 本文主要讲述linux3.10文件系统初始化过程的第二阶段:加载initrd. initrd是一个临时文件系统,由bootload负责加载到内存中,里面包含了基本的可 ...

  7. linux文件系统 - 初始化(一)

    术语表: struct task:进程 struct mnt_namespace:命名空间 struct mount:挂载点 struct vfsmount:挂载项 struct file:文件 st ...

  8. Linux命令备忘录:mount用于加载文件系统到指定的加载点

    mount命令用于加载文件系统到指定的加载点.此命令的最常用于挂载cdrom,使我们可以访问cdrom中的数据,因为你将光盘插入cdrom中,Linux并不会自动挂载,必须使用Linux mount命 ...

  9. linux文件系统 - 初始化(三)

    执行init程序 一.目的 内核加载完initrd文件后,为挂载磁盘文件系统做好了必要的准备工作,包括挂载了sysfs.proc文件系统,加载了磁盘驱动程序驱动程序等.接下来,内核跳转到用户空间的in ...

随机推荐

  1. 创建vue项目 webpack+vue

    # 全局安装 vue-cli $ npm install -g vue-cli # 创建一个基于 "webpack" 模板的新项目 根据提示填写项目信息 && 对项 ...

  2. maven 安装下载与配置 代理设置 《解决下载慢问题》

    maven:下载地址http://mirror.bit.edu.cn/apache/maven/maven-3/ 解压之后配置环境 %maven_home%  d:\*****path 中添加 %ma ...

  3. JavaScript--浅谈DOM操作

    JavaScript之浅谈DOM操作 1.理解DOM: DOM(Document Object Model ,文档对象模型)一种独立于语言,用于操作xml,html文档的应用编程接口. 怎么说,我从两 ...

  4. Kubernetes核心概念简介

    本文将会简单介绍Kubernetes的核心概念.因为这些定义可以在Kubernetes的文档中找到,所以文章也会避免用大段的枯燥的文字介绍.相反,我们会使用一些图表(其中一些是动画)和示例来解释这些概 ...

  5. 可执行文件patch技术&&持续更新

    前言 在ctf比赛中, 有时我们需要对可执行文件进行patch, 或者在植入后门时,patch也是常用的手段.不过手工patch比较麻烦,下面介绍几个工具.本文介绍遇到的各种技术,不断更新. ELF ...

  6. Vue 框架-04-计算属性

    Vue 框架-04-计算属性 计算属性是什么? 大家可以去看官网解释:计算属性和侦听器 今天的第一个小实例: 为啥先放折磨一个实例,之前数据绑定的就已经可以实现了,看起来那么简单,就是为了告诉大家,当 ...

  7. paypal文档

    https://blog.csdn.net/daily886/article/details/73164643?ref=myread.

  8. 如何快速开发一个支持高效、高并发的分布式ID生成器

    ID生成器是指能产生不重复ID服务的程序,在后台开发过程中,尤其是分布式服务.微服务程序开发过程中,经常会用到,例如,为用户的每个请求产生一个唯一ID.为每个消息产生一个ID等等,ID生成器也是进行无 ...

  9. rest framework 的权限管理

    下面是对单个的视图进行的设置的: 请求的时候用postman然后发送信息 我们下面所有的举例都是在用户对Comment这个表的操作 首先先生成一个类似于cookie的字符串 发送给前端浏览器 然后下次 ...

  10. 用 Core Animation 实现图片的碎片化

    用 Core Animation 实现图片的碎片化 参考书籍: 效果如下: 原理其实非常简单哦:). 1. 创建一个CALayer,使用其 contents 属性来装载一张图片(获取图片的CGImag ...