根文件系统的概念
根文件系统是控制权从linux内核转移到用户空间的一个桥梁。linux内核就类似于一个黑匣子,只向用户提供各种功能的接口,但是功能的具体实现不可见,用户程序通过对这些功能接口的不同整合实现不同的功能需求。以用户的角度来说,应用程序调用内核的接口实现不同的功能,此时系统的控制权在用户手中,但是实际上却是先有内核的初始化提供这些接口,用户才可以使用这些接口的,也就是系统的控制权最初应该属于内核。那么控制权是如何从内核转交到用户的呢?通过调用init程序实现,而一般把存在init程序的文件系统称之为根文件系统。
文件系统是基于物理存储设备至上的一种机制,用于存储空间的管理,并维护文件内容与磁盘单元之间的对应关系,便于对文件内容的访问。由前面所述,init程序存储在文件系统之中,如果需要访问init程序必须能够识别对应文件系统的格式(通过挂载实现),而文件系统又建立在物理存储设备之上,所以需要物理存储设备的驱动程序已准备就绪。
文件系统的挂载需要提供挂载点(挂载目录),linux内核在初始化时会初始化一个虚拟的“/”目录用于根文件系统的挂载,其初始化过程如下:
start_kernel
vfs_caches_init()
mnt_init()
init_rootfs()
register_filesystem(&rootfs_fs_type) //注册虚拟的rootfs文件系统
init_mount_tree() //创建“/”目录
bdev_cache_init()
chrdev_init()
rest_init()
kernel_thread(kernel_init, NULL, CLONE_FS)
kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
initramfs和initrd
若init程序存在的物理磁盘设备在内核访问时已准备就绪,内核便可以直接挂载根文件系统并运行init程序,实现kernel空间到用户空间的跳转。但是用户程序可能根据不同的需要存储在诸如IDE、SCSI、USB等多种介质当中,如果将所有的驱动程序都编译进内核,将导致内核异常臃肿,因此,为了使内核适应不同的存储介质,同时不将非必要的驱动程序编译进内核,可使用一种过渡根文件系统。过渡根文件系统与linux内核存放在同一个存储设备中,因此可以直接挂载,而此时该文件系统中的init程序只是加载实际根文件系统的驱动和其他的一些初始化工作,待初始化完成,该init程序会挂载实际的根文件系统,并再次跳转到实际根文件系统中执行实际的init程序。
过渡的根文件系统根据是否直接编译进内核分为initramfs和initrd,而initrd根据文件系统的打包格式又分为cpio-initrd和image-initrd,通过cpio打包的文件系统可以直接释放到“/”,而无需挂载过程,initramfs也是cpio的打包格式。
根文件系统的整体挂载流程如下图所示:
首先,内核根据配置利用过渡根文件系统或默认设置初始化“/”目录;然后检测初始化的“/”目录中是否存在指定应用程序(默认为init程序),如果存在,则直接执行该init程序;否则,根据配置挂载实际根文件系统。与之对应的函数流程为:
kernel_init
kernel_init_freeable()
do_basic_setup()
do_initcalls()
do_initcall_level(level)
[1] do_one_initcall
if (!ramdisk_execute_command) //如果没有定义rdinit参数,则默认执行rootfs中的/init程序
ramdisk_execute_command = "/init";
[2] sys_access((const char __user *) ramdisk_execute_command, 0) //查看rootfs是否有/init程序,若包含则直接运行,否则运行prepare_namespace
ramdisk_execute_command = NULL; //并将ramdisk_execute_command置为NULL
[3] prepare_namespace()
//此时已经处于实际根文件系统的目录下
if (ramdisk_execute_command) { //首先尝试运行rdinit=指定的程序
run_init_process(ramdisk_execute_command);
[4] if (execute_comm