2023-06-11  阅读(1)
原文作者:奇小葩 原文地址:https://blog.csdn.net/u012489236/category_10976532.html

前文中我们已经分析了文件系统,而文件系统的精髓所在是让用户可以通过文件描述符来对指定的inode进行一系列的操作。

本章开始学习伪文件系统,伪文件系统和普通文件系统的区别在于,其inode对用户不可访问,即仅在内核态可见,从用户层的视角来看该文件系统并不存在。伪文件系统的作用是对一些操作系统中的元素进行封装,和普通的文件统一接口,如块设备bdevfs,管道文件pipefs,套接字socketfs等。通过这种方式的统一封装,才实现了Linux一切皆文件的思想。如下图中红色的都应该算是伪文件系统,本章就学习其中的procfs/sysfs/pipefs。

202306111259088121.png

1 proc文件系统

1.1 数据结构

proc文件系统由proc_dir_entry结构体实例组成,内核各子系统可通过接口函数向proc文件系统添加目录和文件。proc_dir_entry结构体定义在fs/proc/internal.h头文件

202306111259102012.png

在VFS中proc文件系统节点由proc_inode结构体表示,其定义在fs/proc/internal.h

202306111259110813.png

1.2 初始化

内核在启动函数start_kernel()中调用proc_root_init()函数完成proc文件系统的初始化工作,函数定义在/fs/proc/root.c文件内,代码如下:

202306111259118394.png

proc_root_init()函数的主要工作是创建proc_inode结构体slab缓存,注册proc_fs_type文件系统类型,为各子系统添加目录项和文件。

202306111259128325.png

proc文件系统需要挂载到根文件系统(通常是/proc挂载点),才能对用户进程可见。那么如何对proc文件系统的挂载,以及用户进程对proc文件系统中文件的操作。

202306111259135026.png

1.3 挂载文件系统

proc文件系统类型挂载函数proc_mount()定义如下(fs/proc/root.c)

202306111259139617.png

其与前面介绍过的文件系统挂载类似,主要是通过proc_fill_super填充超级块实例,创建设置denry、proc_inode实例等,主要来看看proc_fill_super函数实现(fs/proc/inode.c)

202306111259145828.png

内核在/fs/proc/root.c文件内静态创建了proc文件系统根目录项的proc_dir_entry实例proc_root:

202306111259158329.png

proc_fill_super()函数首先对super_block实例进行初始化,设置其超级块操作结构实例为 proc_sops ,然后调用proc_get_inode()函数创建proc_inode实例,并建立其与proc_root实例的关联,创建文件系统根目录项dentry实例,并关联proc_inode.vfs_inode成员(inode实例),最后在根目录下创建self和thread-self目录项。所以其数据结构如下图所示

2023061112591657510.png

proc文件系统需要由用户挂载,才能对用户可见。通常在操作系统启动脚本中会将proc文件系统挂载到/proc目录下。

  • 挂载操作将创建proc文件系统超级块super_block实例,文件系统根目录项dentry实例,以及proc_inode实例。proc_inode实例中包含节点inode结构体成员,其i_op、i_fop成员分别指向proc_root实例中proc_iops、proc_fops指向的实例。
  • 对于根节点,proc_iops、proc_fops指向的实例是专用的,它们分别为proc_root_inode_operations、proc_root_operations。
  • 用户打开proc文件系统中文件时,将为每个路径分量(目录项)创建dentry和proc_inode实例,并建立proc_inode实例与proc_dir_entry实例之间的关联。

1.4 添加普通目录项

向proc文件系统添加普通目录项的接口函数为proc_mkdir_data(),定义如下(fs/proc/generic.c)

2023061112591716711.png

设置普通目录文件的节点操作结构和文件操作结构实例为proc_dir_inode_operations和proc_dir_operations,这两个实例都是通用的实例,最后调用proc_register()函数注册proc_dir_entry实例,主要是将其添加到父目录项管理的红黑树中。

向proc文件系统添加文件的操作与添加普通目录项的操作类似,主要区别是添加文件时,需要定义文件操作结构file_operations实例,传递给proc_dir_entry实例。

2023061112591807812.png

对于普通目录项,proc_inode内嵌inode实例i_op和i_fop成员赋值为proc_dir_entry实例proc_iops、proc_fops成员值。对于文件目录项,proc_inode内嵌inode实例i_op赋值为proc_dir_entry实例proc_iops成员值,而i_fop成员指向 proc_reg_file_ops 实例,

2023061112591891913.png

1.6 打开文件

proc文件系统中路径搜索都是从其根目录项开始的,根目录项与普通目录项关联inode_operations实例不同,搜索函数也不同,如下图所示。

2023061112591961914.png

根目录项关联inode_operations实例定义如下(fs/proc/root.c):

2023061112592031315.png

普通目录项关联inode_operations实例定义如下(fs/proc/generic.c):

2023061112592107516.png

在打开文件的操作中将调用proc_get_inode()函数为proc_dir_entry实例创建proc_inode实例,如果实例代表的是普通文件,则inode实例关联的文件操作结构实例设为proc_reg_file_ops,定义如下:

2023061112592166017.png

打开文件后,需要读写文件,其也就可以调用该接口的read/write接口,其整个流程如下

2023061112592238718.png

2. sysfs

sys文件系统与proc文件系统一样,通过内核数据结构实例,组织成文件系统,它由kernfs_node结构体实例构成。

kernfs文件系统并不是一个完整的文件系统,它只是提供构建文件系统的基础组件及相关的接口函数,使用kernfs文件系统的子系统需要定义并注册文件系统类型file_system_type实例。其过程与procfs文件基本类似就不单独介绍。

3 pipefs

管道(pipe)和命名管道是进程间通信的机制,用于进程间的单向数据传输。管道和命名管道的两端分别是写进程和读进程,本质上是一种特殊的文件,文件的内容保存在一个内存的缓冲区中(FIFO)。

管道没有文件名称,由内核管理,只能用于同源的进程间(fork()出来的进程间)通信,对其它非同源的进程不可见。命名管道与普通文件一样具有文件名称,文件保存在具体文件系统中,导出到内核根文件系统,对用户可见,可用于任意进程之间的通信。

管道/命名管道包含一个缓存区,缓存区可认为有一个进口和一个出口,写进程从进口写入数据,读进程从出口读取数据,数据在缓存区中按写入时间先后顺序排列,先进的数据先读出,只能按顺序读取,不能任意读取。

2023061112592323419.png

父进程创建子进程后,表示管道的两个文件描述符将传递给子进程。如果父进程要通过管道向子进程传递数据,则关闭父进程读端文件描述符和子进程写端文件描述符。父进程则可以向管道写入数据,子进程可从管道读取数据。

pipefs伪文件系统类型pipe_fs_type实例定义如下(/fs/pipe.c)

2023061112592389120.png

pipefs伪文件系统初始化函数init_pipe_fs()中注册了文件系统类型,并执行了内核挂载,函数定义如下:

2023061112592441821.png

其pipefs_mount比较简单,主要工作是创建挂载mount结构体实例,调用文件系统类型中定义的mount()函数,创建超级块super_block、根目录项dentry和inode结构体实例,并建立以上数据结构实例之间的关系,执行结果如下图所示。

2023061112592523322.png

我们来学习下如何使用的管道,用户进程创建管道的系统调用为pipe和pipe2,实现如下

2023061112592617523.png

pipe2()系统调用实现函数定义如下:

2023061112592675624.png

pipe()/pipe2()系统调用将返回2个文件描述符,参数fildes指向的数组用于存放返回的文件描述符,fildes[0]表示读取端文件描述符(只读形式),fildes[1]表示写入端文件描述符(只写形式)。

2023061112592746125.png

系统调用内通过__do_pipe_flags()函数创建表示管道的dentry和inode实例,分配并初始化表示管道的pipe_inode_info结构体实例,分配2个file实例,2个文件描述符,2个file实例关联到同一个inode实例,file和inode关联的文件操作结构实例为 pipefifo_fops

2023061112592840426.png


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文