文件系统加载过程Word文档下载推荐.docx
- 文档编号:19099556
- 上传时间:2023-01-03
- 格式:DOCX
- 页数:20
- 大小:537.89KB
文件系统加载过程Word文档下载推荐.docx
《文件系统加载过程Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《文件系统加载过程Word文档下载推荐.docx(20页珍藏版)》请在冰豆网上搜索。
2、加载initrd;
3、挂载磁盘文件系统;
二、常用数据结构
linux文件系统中重要的数据结构有:
文件、挂载点、超级块、目录项、索引节点等。
每个数据结构的具体实现请参见源代码,这里不再描述。
为了直观的表示数据结构之间的关系,请参见图1:
图中含有两个文件系统(红色和绿色表示的部分),并且绿色文件系统挂载在红色文件系统tmp目录下。
一般来说,每个文件系统在VFS层都是由挂载点、超级块、目录和索引节点组成;
当挂载一个文件系统时,实际也就是创建这四个数据结构的过程,因此这四个数据结构的地位很重要,关系也很紧密。
由于VFS要求实际的文件系统必须提供以上数据结构,所以不同的文件系统在VFS层可以互相访问。
如果进程打开了某个文件,还会创建file(文件)数据结构,这样进程就可以通过file来访问VFS的文件系统了。
另外,该图只给出了主要的关系结构,忽略了部分细节。
图1
三、函数调用关系
图2描述了文件系统初始化过程中主要的函数调用关系。
linux文件系统初始化过程主要分为三个阶段:
1、vfs_caches_init()负责挂载rootfs文件系统,并创建了第一个挂载点目录:
'
/'
;
2、rest_init()负责加载initrd文件,扩展VFS树,创建基本的文件系统目录拓扑;
3、init程序负责挂载磁盘文件系统,并将文件系统的根目录从rootfs切换到磁盘文件系统;
图2
四、总结
linux文件系统初始化过程主要分为三个阶段:
挂载rootfs,提供第一个挂载点'
/;
加载initrd,扩展VFS树;
执行init程序,完成linux系统的初始化。
下面会详细介绍每个阶段的主要内容。
linux文件系统初始化过程
(2)---挂载rootfs文件系统
本文主要讲述linux3.10文件系统初始化过程的第一阶段:
挂载rootfs文件系统。
rootfs是基于内存的文件系统,所有操作都在内存中完成;
也没有实际的存储设备,所以不需要设备驱动程序的参与。
基于以上原因,linux在启动阶段使用rootfs文件系统,当磁盘驱动程序和磁盘文件系统成功加载后,linux系统会将系统根目录从rootfs切换到磁盘文件系统。
二、主要函数调用过程
图1描述了挂载rootfs的函数调用关系(图中红色部分),便于后面的分析。
从图中发现,在挂载rootfs前会先挂载sysfs,这样做的原因是确保sysfs能够完整的记录下设备驱动模型。
sysfs_init()完成注册和挂载sysfs文件系统的功能;
init_rootfs()负责注册rootfs,init_mount_tree()负责挂载rootfs,并将init_task的命名空间与之联系起来。
三、linux文件系统初始化
vfs_cache_init()首先建立并初始化目录hash表dentry_hashtable和索引节点hash表inode_hashtable;
然后设置内核可以打开的最大文件数;
最后调用mnt_init()完成sysfs和rootfs文件系统的注册和挂载。
linux使用哈希表存储目录和索引节点,以提高目录和索引节点的查找效率;
dentry_hashtable是目录哈希表,inode_hashtable是索引节点哈希表。
四、挂载sysfs文件系统
sysfs用来记录和展示linux驱动模型,sysfs先于rootfs挂载是为全面展示linux驱动模型做好准备。
mnt_init()调用sysfs_init()注册并挂载sysfs文件系统,然后调用kobject_create_and_add()创建"
fs"
目录。
下面详细介绍sysfs文件系统的挂载过程:
1、sysfs_init()调用register_filesystem()注册文件系统类型sysfs_fs_type,并加入到全局单链表file_systems中。
sysfs_fs_type定义如下,.mount成员函数负责超级块、根目录和索引节点的创建和初始化工作。
173err=register_filesystem(&
sysfs_fs_type);
174if(!
err){
175sysfs_mnt=kern_mount(&
176if(IS_ERR(sysfs_mnt)){
177printk(KERN_ERR"
sysfs:
couldnotmount!
\n"
);
178err=PTR_ERR(sysfs_mnt);
179sysfs_mnt=NULL;
180unregister_filesystem(&
181gotoout_err;
182}
2、sysfs_init()->
kern_mount()->
vfs_kern_mount()创建并初始化structmount挂载点,并使用全局变量sysfs_mnt保存该挂载点的挂载项(mnt成员)。
3、kern_mount()调用sysfs_fs_type的.mount成员sysfs_mount()创建并初始化超级块、根目录'
、根目录的索引节点等数据结构;
并且把超级块添加到全局单链表super_blocks中,把索引节点添加到hash表inode_hashtable和超级块的inode链表中。
目前,我们可以得出一个重要结论:
kern_mount()主要完成挂载点、超级块、根目录和索引节点的创建和初始化操作,可以看成是一个原子操作,这个函数以后会频繁使用。
796mnt->
mnt.mnt_root=root;
797mnt->
mnt.mnt_sb=root->
d_sb;
798mnt->
mnt_mountpoint=mnt->
mnt.mnt_root;
799mnt->
mnt_parent=mnt;
5、mnt_init()调用kobject_create_and_add()创建"
通过以上步骤,sysfs文件系统在VFS中的视图如图2所示:
挂载点指向超级块和根目录;
超级块处在super_blocks单链表中,并且链接起所有属于该文件系统的索引节点;
根目录'
和目录"
指向各自的索引节点;
为了提高查找效率,索引节点保存在hash表中。
图2
五、挂载rootfs文件系统
mnt_init()调用init_rootfs()注册rootfs,然后调用init_mount_tree()挂载rootfs。
下面详细介绍rootfs文件系统的挂载过程:
1、mnt_init()调用init_rootfs()注册文件系统类型rootfs_fs_type,并加入到全局单链表file_systems中。
rootfs_fs_type定义如下,mount成员函数负责超级块、根目录和索引节点的建立和初始化工作。
2、init_mount_tree()调用vfs_kern_mount()挂载rootfs文件系统,详细的挂载过程与sysfs文件系统类似,不再赘述。
3、init_mount_tree()调用create_mnt_ns()创建命名空间,并设置该命名空间的挂载点为rootfs的挂载点,同时将rootfs的挂载点链接到该命名空间的双向链表中。
4、init_mount_tree()设置init_task的命名空间,同时调用set_fs_pwd()和set_fs_root()设置init_task任务的当前目录和根目录为rootfs的根目录'
。
通过以上分析,我们发现sysfs和rootfs的区别在于:
虽然系统同时挂载了sysfs和rootfs文件系统,但是只有rootfs处于init_task进程的命名空间内,也就是说系统当前实际使用的是rootfs文件系统。
此时,sysfs和rootfs在VFS中的视图如图3所示:
为了突出主要关系,省略了挂载点指向超级块和根目录。
从图中看出,rootfs处于进程的命名空间中,并且进程的fs_struct数据结构的root和pwd都指向了rootfs的根目录'
,所以用户实际使用的是rootfs文件系统。
另外,rootfs为VFS提供了'
根目录,所以文件操作和文件系统的挂载操作都可以在VFS上进行了。
图3
六、总结
linux文件系统在初始化时,同时挂载了sysfs和rootfs文件系统,但是只有rootfs处于进程的命名空间中,且进程的root目录和pwd目录都指向rootfs的根目录。
至此,linux的VFS已经准备好了根目录(rootfs的根目录'
),此时用户可以使用系统调用对VFS树进行扩展。
linux文件系统初始化过程(3)---加载initrd(上)
本文主要讲述linux3.10文件系统初始化过程的第二阶段:
加载initrd。
initrd是一个临时文件系统,由bootload负责加载到内存中,里面包含了基本的可执行程序和驱动程序。
在linux初始化的初级阶段,它提供了一个基本的运行环境。
当成功加载磁盘文件系统后,系统将切换到磁盘文件系统并卸载initrd。
如果是嵌入式设备,那么最终的文件系统就是initrd。
二、cpio文件格式
initrd常用的的文件格式是cpio,cpio格式记录了文件系统的结构和内容。
cpio格式具体定义如图1所示:
cpio格式的文件由段组成,最后一个段比较特殊,文件名为”TRAILER!
!
”。
每个段都由文件头、文件名和文件体组成;
文件名和文件体的长度由文件头中的name_len和body_len指定,并且文件名和文件体需要按指定字节对齐,所以尾部包含padding。
文件头共110个字节,头6个字节固定为070701,剩下字节的含义分别为:
索引节点号、文件模式、用户id、组id、链接数、时间戳、文件体长度、主设备号、次设备号、设备号、文件名长度、保留字段。
其他详细情况请参见init/initramfs.c文件,这里不再描述。
图1
三、initrd文件实例
为了更直观的理解cpio格式的initrd文件,下面看一个实例。
在ubuntu环境中,boot目录下存放着经过压缩的cpio格式文件initrd。
将boot目录下的initrd文件拷贝到任意目录下,重名为为initrd.gz,并且使用gunzip解压。
这样我们就得到了一个cpio格式的initrd文件,使用vi查看文件内容如图2所示(由于文件太大,只展示了部分内容):
简单分析后显示该文件包含了script/nfs-top目录、script/nfs-top/ORDER文件、script/nfs-top/udev文件、run目录、标志cpio结束的TRAILER!
文件。
四、解压initrd文件
initrd经过gunzip解压后,可以使用cpio工具解压cpio格式的文件。
命令如下:
1.root:
cpio-idmv
<
initrd
解压成功后,使用ls命令查看initrd文件内容如图3所示:
bin和sbin目录下包含基本的可执行程序;
conf和etc目录下是配置文件;
lib目录下是可执行程序使用的动态库;
scripts目录下是脚本程序;
init程序。
initrd必须提供一个init程序,linux在加载完initrd后,会跳转到init程序,由init程序负责后面的初始化工作。
五、总结
本文详细介绍了cpio格式的initrd文件,以及解压后各个目录的含义。
initrd文件系统提供了init程序,在linux初始化阶段的后期会跳转到init程序,由该程序负责加载驱动程序和挂载磁盘文件系统以及其他的初始化工作。
linux文件系统初始化过程(4)---加载initrd(中)
上文详细介绍了CPIO格式的initrd文件,本文从源代码角度分析加载并解析initrd文件的过程。
initrd文件和linux内核一般存储在磁盘空间中,在系统启动阶段由bootload负责把磁盘上的内核和initrd加载到指定的内存空间中;
然后,再由内核读取和解析initrd文件,在VFS(目前只有rootfs的根目录)中新建目录、常规文件、符号链接文件以及特殊文件;
这样VFS就从根目录"
/"
成长为一棵枝繁叶茂的大树了。
二、函数调用过程
initrd详细的加载过程在init/initramfs.c中实现的,为了更好的理解加载过程,我们给出了关键函数的调用关系图1。
这里需要注意下,由于使用roofs_initcall()宏在initcallroofs段中注册了populate_rootfs()函数,因此在执行do_initcalls()函数时会隐示调用populate_rootfs()。
三、initcall简介
linux在代码段中定义了一个特殊的段initcall,该段中存放的都是函数指针;
linux初始化阶段调用do_initcalls()依次执行该段的函数。
关于该段的详细信息可以参见vmlinux.lds.S链接脚本。
用户可以调用以下一组宏在initcall段中注册函数指针;
initcall段分为initcall0-initcall7这8个等级,initcall0段的优先级最高,initcall7段的优先级最低,优先级高的段最先被执行;
initcallrootfs段优先级介于5和6之间。
#define__define_initcall(fn,id)\
179staticinitcall_t__initcall_##fn##id__used\
180__attribute__((__section__("
.initcall"
#id"
.init"
)))=fn
187#defineearly_initcall(fn)__define_initcall(fn,early)
196#definepure_initcall(fn)__define_initcall(fn,0)
198#definecore_initcall(fn)__define_initcall(fn,1)
199#definecore_initcall_sync(fn)__define_initcall(fn,1s)
200#definepostcore_initcall(fn)__define_initcall(fn,2)
201#definepostcore_initcall_sync(fn)__define_initcall(fn,2s)
202#definearch_initcall(fn)__define_initcall(fn,3)
203#definearch_initcall_sync(fn)__define_initcall(fn,3s)
204#definesubsys_initcall(fn)__define_initcall(fn,4)
205#definesubsys_initcall_sync(fn)__define_initcall(fn,4s)
206#definefs_initcall(fn)__define_initcall(fn,5)
207#definefs_initcall_sync(fn)__define_initcall(fn,5s)
208#definerootfs_initcall(fn)__define_initcall(fn,rootfs)
209#definedevice_initcall(fn)__define_initcall(fn,6)
210#definedevice_initcall_sync(fn)__define_initcall(fn,6s)
211#definelate_initcall(fn)__define_initcall(fn,7)
212#definelate_initcall_sync(fn)__define_initcall(fn,7s)
用户使用不同优先级的initcall宏可以很方便的在linux代码中注册函数指针;
将这些函数指针存储在相应的initcall段中;
最终,由do_initcalls()按照优先级依次执行段中的函数,具体的代码实现如下:
715staticinitcall_t*initcall_levels[]__initdata={
716__initcall0_start,
717__initcall1_start,
718__initcall2_start,
719__initcall3_start,
720__initcall4_start,
721__initcall5_start,
722__initcall6_start,
723__initcall7_start,
724__initcall_end,
725};
678int__init_or_moduledo_one_initcall(initcall_tfn)
679{
681intret;
686ret=fn();
}
739staticvoid__initdo_initcall_level(intlevel)
740{
742initcall_t*fn;
...
751for(fn=initcall_levels[level];
fn<
initcall_levels[level+1];
fn++)
752do_one_initcall(*fn);
753}
754
755staticvoid__initdo_initcalls(void)
756{
757intlevel;
758
759for(level=0;
level<
ARRAY_SIZE(initcall_levels)-1;
level++)
760do_initcall_level(level);
761}
回到加载initrd这个话题中,在init/initram.c的最后使用rootfs_initcall宏注册了populate_rootfs()函数;
基于以上分析,我们知道这里就是加载initrd文件的入口,下面就开始分析该函数的功能。
627rootfs_initcall(populate_rootfs);
四、加载initrd文件
系统启动阶段,bootload将initrd加载到内存起始地址为initrd_start,结束地址为initrd_end的内存中。
populate_rootfs()调用unpack_to_rootfs()从内存中读取并解析initrd文件;
根据CPIO的格式我们知道initrd文件是由很多个段组成,且段中又是由文件头、文件名和文件体组成,因此该解析程序可以使用了状态机原理处理initrd文件。
解析程序定义了以下8种状态:
Start(初始状态)、Collect(获取符号链接文件信息状态)、GotHeader(获取文件头信息状态)、SkipIt(跳过该段状态)、GotName(获取文件名并新建文件状态)、CopyFile(写文件状态)、GotSymlink(新建符号链接文件状态)、Reset(终止状态)。
376static__initdataint(*actions[])(void)={
377[Start]=do_start,
378[Collect]=do_collect,
379[GotHeader]=do_header,
380[SkipIt]=do_skip,
381[GotName]=do_name,
382[CopyFile]=do_copy,
383[GotSymlink]=do_symlink,
384[Reset]=do_reset,
385};
为了直观理解initrd文件的解析过程,下面给出状态机跳转图2。
从图中可以看出将文件分为符号链接和非符号链接两种情况处理,这是因为符号链接文件是一种特殊的文件,只有第一个符号链接文件的inode存储的是真实数据,而其他符号链接文件inode中存储的是第一个符号链接文件的路径名,因此需要把第一个符号链接文件的路径名缓存起来,缓存的数据结构是hash表,所以在处理符号链接文件时多了一些hash表的操作,因此分为了符号链接文件和非符号链接文件这两种情况来处理。
initrd文件的详细解析过程如下:
1、S0:
初始状态,初始化一些全局变量;
2、S1:
获取符号链接文件的文件头和文件体;
3、S2:
根据CPIO格式的定义,获取文件头信息;
4、S3:
跳过当前CPIO格式的段,继续处理下一个段;
5、S4:
获取文件名,并在VF
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 文件系统 加载 过程