linux文件系统的启动过程分析
启动参数:
chosen {
bootargs = "root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw console=ttymxc3,115200n8 lpj=7905280 initroot=10:none,initramfs,/startup/run quiet";
};
prepare_namespace
函数功能:
从设备文件路径名到处设备标识符,调用mount_root安装根文件系统
函数源码:
/*
* Prepare the namespace - decide what/where to mount, load ramdisks, etc.
*/
void __init prepare_namespace(void)
{
int is_floppy;
if (root_delay) { // 如果启动参数中设置了安装根文件系统的延迟参数rootdelay,
// 则调用ssleep延迟安装root_delay秒
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
/*
* wait for the known devices to complete their probing
*
* Note: this is a potential source of long boot delays.
* For example, it is not atypical to wait 5 seconds here
* for the touchpad of a laptop to initialize.
*/
wait_for_device_probe();
md_run_setup(); // 初始化MD设备,MD设备主要包含了LINUX内核的软RAID实现
#ifdef CONFIG_SUPPORT_INITROOT
try_initroot();
#endif
if (saved_root_name[0]) { // 由启动参数的解析得到,saved_root_name = /dev/mmcblk0p2
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
if (initrd_load()) // 具体分析参见本文
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; // 通过设备文件标识符ROOT_DEV判断启动设备文件是否是软盘,
// 并把判断结果存入局部变量is_floppy中
// 在我们的代码中,该代码不成立
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root(); // 调用函数mount_root安装实际根文件系统,参见本文分析
out:
devtmpfs_mount("dev");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
}
mount_root
函数源码:
void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS // 网络文件系统,我们不用
if (ROOT_DEV == Root_NFS) {
if (mount_nfs_root())
return;
printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
ROOT_DEV = Root_FD0;
}
#endif
#ifdef CONFIG_BLK_DEV_FD // 软盘文件系统,我们不用
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
/* rd_doload is 2 for a dual initrd/ramload setup */
if (rd_doload==2) {
if (rd_load_disk(1)) {
ROOT_DEV = Root_RAM1;
root_device_name = NULL;
}
} else
change_floppy("root floppy");
}
#endif
#ifdef CONFIG_BLOCK
create_dev("/dev/root", ROOT_DEV); // 调用create_dev函数在/dev目录中创建root设备文件,设备标识符为ROOT_DEV
mount_block_root("/dev/root", root_mountflags); // 调用函数mount_block_root安装根文件系统,具体分析见下文
#endif
}
name_to_dev_t
函数功能:
把设备路径名转换成设备标识符
函数参数:
name:设备路径名
函数源码:
/*
* Convert a name into device number. We accept the following variants:
*
* 1) device number in hexadecimal represents itself
* 2) /dev/nfs represents Root_NFS (0xff)
* 3) /dev/<disk_name> represents the device number of disk
* 4) /dev/<disk_name><decimal> represents the device number
* of partition - device number of disk plus the partition number
* 5) /dev/<disk_name>p<decimal> - same as the above, that form is
* used when disk name of partitioned disk ends on a digit.
* 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
* unique id of a partition if the partition table provides it.
* The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
* partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
* filled hex representation of the 32-bit "NT disk signature", and PP
* is a zero-filled hex representation of the 1-based partition number.
* 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
* a partition with a known unique id.
*
* If name doesn't have fall into the categories above, we return (0,0).
* block_class is used to check if something is a disk name. If the disk
* name contains slashes, the device name has them replaced with
* bangs.
*/
dev_t name_to_dev_t(char *name)
{
char s[32];
char *p;
dev_t res = 0;
int part;
#ifdef CONFIG_BLOCK
if (strncmp(name, "PARTUUID=", 9) == 0) {
name += 9;
res = devt_from_partuuid(name);
if (!res)
goto fail;
goto done;
}
#endif
/* 如果设备文件路径名不以“/dev/”为前缀,
* 则从设备文件路径名中直接取主设备号和次设备号,
* 组成设备文件标识符并返回 */
if (strncmp(name, "/dev/", 5) != 0) {
unsigned maj, min;
if (sscanf(name, "%u:%u", &maj, &min) == 2) {
res = MKDEV(maj, min);
if (maj != MAJOR(res) || min != MINOR(res))
goto fail;
} else {
res = new_decode_dev(simple_strtoul(name, &p, 16));
if (*p)
goto fail;
}
goto done;
}
/* 如果设备路径名中的设备文件名等于”nfs”,说明根文件系统是网络文件系统,
* 返回网络文件系统的设备文件标识符 */
name += 5;
res = Root_NFS;
if (strcmp(name, "nfs") == 0)
goto done;
/* 如果设备路径名中的设备文件名等于“ram”,说明根文件系统是ram文件系统,
* 返回内存文件系统的设备文件标识符 */
res = Root_RAM0;
if (strcmp(name, "ram") == 0)
goto done;
if (strlen(name) > 31)
goto fail;
strcpy(s, name);
/* 把设备文件名中的” /”替换为”!” */
for (p = s; *p; p++)
if (*p == '/')
*p = '!';
res = blk_lookup_devt(s, 0); // 以设备文件名为参数调用blk_lookup_devt获取设备的标识符,
// 如果成功则跳到最后一步
if (res)
goto done;
/*
* try non-existent, but valid partition, which may only exist
* after revalidating the disk, like partitioned md devices
*/
while (p > s && isdigit(p[-1]))
p--;
if (p == s || !*p || *p == '0')
goto fail;
/* try disk name without <part number> */
part = simple_strtoul(p, NULL, 10);
*p = '\0';
res = blk_lookup_devt(s, part);
if (res)
goto done;
/* try disk name without p<part number> */
if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
goto fail;
p[-1] = '\0';
res = blk_lookup_devt(s, part);
if (res)
goto done;
fail:
return 0;
done:
printk("---------------[%s][%d] : %d\n", __FILE__, __LINE__, res);
return res;
}
initrd_load
函数源码:
int __init initrd_load(void)
{
if (mount_initrd) { // 如果mount_initrd=0,则跳过initrd机制,直接挂载常规的根文件系统
create_dev("/dev/ram", Root_RAM0);
/*
* Load the initrd data into /dev/ram0. Execute it as initrd
* unless /dev/ram0 is supposed to be our actual root device,
* in that case the ram disk is just set up here, and gets
* mounted in the normal path.
*/
// 用rd_load_image函数把/initrd.image(也就是initrd的实际数据)挂载到/dev/ram0,
// 接着判断根设备是否就是/dev/ram0,如果是则跳过initrd处理,
// 按正常流程挂载根文件系统;否则用initrd_handle函数进行initrd处理
// 我们的系统在 rd_load_image("/initrd.image") 不符合,所以最终还是按照正常流程来挂载根文件系统
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
sys_unlink("/initrd.image");
handle_initrd();
return 1;
}
}
sys_unlink("/initrd.image");
return 0;
}