switch_root源码分析

云计算分享者 2024-02-23 03:32:33
背景

在分析linux的启动过程的时候,少不了对switch-root的分析.

现在Linux启动都会使用一个cpio+gzip的initramfs.img文件,一般在/boot目录下面,在grub的配置文件里面通过initrd16引入.

kernel启动完成之后,会弄一个rootfs(要么是ramfs,要么是tmpfs)文件系统,然后将这个img文件解压到这个rootfs里面.然后里面的/init就会执行.其会完成对各种驱动的加载(主要是磁盘驱动),然后就可以找到root=xxx指定的分区,然后将这个分期挂载上,最后使用switch-root切换到真正的根分区,然后真正的根分区里面的init(1)会接着跑自己的东西.

这里面对centos5/6/7有些实现上的区别,在5/6里面,initramfs.img里面的/init是一个bash脚本,在这个脚本里面完成相应的操作,最后调用switch_root命令来完成切换.这个switch_root命令包含在util-linux包里面.

centos7完全使用systemd了,initramfs是用dracut生成的,其/init是一个到systemd的软连接,最后调用的是systemctl switch-root来完成的切换,类似如下的命令. 这儿只分析包含在util-linux中的switch_root的源代码,systemctl的switch-root子命令的源代码完成的工作是类似的.

/usr/bin/systemctl --no-block --force switch-root /sysroot

switch_root用法

switch_root的用法基本如下,需要newrootdir和切换之后的要执行的init(1)的路径,以及需要的传递的参数.

switch_root /sysroot "/sbin/init"

源代码分析

代码分为3个主要部分.先得到参数,然后调用switchroot函数完成最重要的切换操作,再调用execv将当前进程替换为新的init进程.

switchroot先使用mount --move将几个特殊的挂载点/dev,/proc,/sys,/run移动到newroot(/sysroot)下面.所以在调用switchroot之前,一定不要将这几个挂载点umount了,然后在newroot下面一定要存在这几个目录. 这儿使用bind mount其实也是可以的.

然后是进行目录切换,这儿是代码里面比较精细的地方,也需要仔细的阅读.

这儿会将老的rootfs的根目录进行一次open,得到一个文件描述符,然后才move mount一次,将newroot(/sysroot) move mount到/,完成之后,在chroot到.,这样老的rootfs只能通过cfd访问到了.

这儿需要保持一个cfd(对老的rootfs的引用),是因为老的rootfs里面的内容现在占用了一块内存来存放从initramfs.img中解压过来的内容,后面这些内容已经不需要了,所以需要被删除.

rootfs基本都是使用tmpfs或者ramfs,在这种情况下,通过recursiveRemove 删除里面的内容.

删除的代码就是一个递归函数的写法了,需要注意的是下面的判断确保递归的时候不跨越文件系统,导致newroot里面的东西被删除了.

注意switch-root的实现里面没有使用pivot_root(2)这个系统调用来直接交换rootfs里面的/和/sysroot,而是使用的几次move mount和chroot实现了/sysroot对/的overmount.

0 阅读:0

云计算分享者

简介:感谢大家的关注