switch_root切换rootfs实例
将可读写的rootfs进行overlay到只读rootfs,形成新的rootfs。通过switch_root将跟文件系统切换的overlay结果的rootfs上。进而形成对只读rootfs的保护,也可以进行读写。
1 switch_root用法
switch_root切换跟文件系统,必须保证作为PID 1执行:
switch_root [-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]
Free initramfs and switch to another root fs: chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /, execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint. -c DEV Reopen stdio to DEV after switch
NEW_ROOT:是最终挂载的跟文件系统。
NEW_INIT:是最终根文件系统的init程序。
-c /dev/console:是可选参数。
2 使用switch_root
2.1 kernel的init进程调用
kernel在kernel_init()中调用init进程,会依次尝试多个init程序。如果/init存在,则会被第一个获的执行机会。
start_kernel
arch_call_rest_init
rest_init
kernel_init--启动第一个用户空间进程,所调用的init次序依次是:
->kernel_init_freeable
->init_eaccess--判断init_eaccess是否存在,如果不存在则将ramdisk_execute_command置为NULL。
->ramdisk_execute_command--默认值为/init,可以在bootargs中通过"rdinit=xxx"指定。
->execute_command--默认值为"",可以通过"init=xxx"指定。
->CONFIG_DEFAULT_INIT--在编译选项中配置。
->/sbin/init
->/etc/init
->/bin/init
->/bin/sh
->panic--如果以上init都未能成功执行,则进入panic()。
2.2 编写init脚本
由于switch_root必须由PID为1的进程调用,所以init作为脚本,并在脚本中使用exec执行。
编写ramfs的/init脚本:
#!/bin/sh
mount -t ubifs ubi0:rootfs /rootfs/ -o ro
result=$?
if [ $result -ne 0 ]; then
return 1
fi
mount -t ubifs ubi2:userdata /userdata/
result=$?
if [ $result -ne 0 ]; then
return 1
fi
mount -t overlay -o lowerdir=/rootfs,upperdir=/userdata/rootfs,workdir=/userdata/work overlay /rootfs
result=$?
if [ $result -ne 0 ]; then
return 1
fi
exec switch_root -c /dev/console /rootfs/ /sbin/init--exec确保使用switch_root替代了当前的/init,所以switch_root的PID为1;switch_root通过execv()调用/sbin/init程序。确保/sbin/init的PID也为1。
result=$?
if [ $result -ne 0 ]; then
return 1
fi
/rootfs作为只读跟文件系统;/userdata/rootfs作为可写跟文件系统,对/rootfs进行overlay,并将/rootfs作为最终overlay结果。/userdata/work作为临时工作目录。
最终通过exec switch_root切换到/rootfs作为/跟文件系统,并且调用/sbin/init作为新的init程序。
联系方式:arnoldlu@qq.com