Linux系统rootfs切换到真正的根文件系统详细源码解析(附工作项目手动制作根文件实例)

一、简介

最近有一块开发板需要制作一个定制的Linux操作系统,通过该项目总结一下相关的资料,包含rootfs切换到真正的根文件系统、如何制作根文件系统和相关的启动配置
机器简介:基于ARM架构的A53开发板,集成了本公司自研主控芯片,采用uboot引导,系统安装在ssd上,内核从nand flash启动;

二、设计过程总结

1、busybox制作精简的文件系统并完成ramfs启动;
2、通过精简的文件系统制作完整的文件系统并拷贝到硬盘;
3、rootfs切换到真正的根文件配置;
4、启动参数设置;

2.1 busybox制作精简的文件系统并完成ramfs启动

1、首先下载busybox源码:
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
2、修改启动配置;

vim etc/inittab 
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::restart:/sbin/init
vim etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys

3、利用busybox小环境在内核中编译生产Image.gz;

make -j4 defconfig
make INSTALL_MOD_PATH=$rootfs  modules_install  //rootfs为busybox小环境
make -j4 vmlinux Image Image.gz "$@"
make dtbs

4、uboot中启动Image.gz可以将文件系统以ramfs的形式挂载在内存中;

2.2 通过精简的文件系统制作完整的文件系统并拷贝到硬盘

1、本次制作的完整文件系统是直接在其他机器上交叉下载相关的文件系统生成一个arm架构的根文件系统,然后再打包到硬盘中;

sudo apt-get install debian-archive-keyring
sudo apt-get install binfmt-support qemu qemu-user-static debootstrap 
sudo debootstrap --arch=arm64 --foreign stretch debian_arm64_stretch http://mirrors.ustc.edu.cn/debian/
sudo cp /usr/bin/qemu-aarch64-static debian_arm64_stretch/usr/bin

2、大致的过程就是先下载arm平台可以交叉chroot的环境,本次采用x86机器操作,然后chroot到arm环境中下载相关的包后再退出即可;

./ch-mount.sh  debian_arm64_stretch/
cat ./ch-mount.sh
#!/bin/bash
    echo "MOUNTING"
    sudo mount -t proc /proc ${
    
    1}proc
    sudo mount -t sysfs /sys ${
    
    1}sys
    sudo mount -o bind /dev ${
    
    1}dev
    sudo mount -o bind /dev/pts ${
    
    1}dev/pts
    sudo chroot ${
    
    1}

3、下载相关的包

apt update
apt install sudo
apt install ssh
apt install net-tools
apt install ethtool 
apt install ifupdown
apt install iputils-ping
apt install rsyslog
apt install htop 
apt install vim

注意:没有源的话要手动添加到etc/apt/sources.list文件中

4、打包到硬盘
将启动硬盘挂载到其他系统中格式化成ext4:

mkfs.ext4 /de/sdb 
fdisk /de/sdb  //创建一个根分区设置100G(根据实际硬盘大小设置,建议在30G以上)
mkfs.ext4  /dev/sdb1 
将制作的根文件系统拷贝到sdb1

本人博客:分区和打包根文件系统

2.3 rootfs切换到真正的根文件配置

可以直接在busybox环境中的/init添加,或者直接覆盖原来的etc/init.d/rcS

vim /init
#!/bin/bash
echo "Hello Linux"
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export ROOTMNT=/root
export ROFLAG=-r

mount -n -t devtmpfs udev /dev
mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys
mount -n -t ramfs ramfs /run
udevd --daemon
udevadm trigger --action=add
udevadm settle
for x in $(cat  /proc/cmdline); do
	case $x in 
	root=*)
		ROOT=${
    
    x#root=}
		;;
	ro)
		ROFLAG=-r
		;;
	rw)
		ROFLAG=-w
		;;
	esac
done

mount ${
    
    ROFLAG} ${
    
    ROOT} ${
    
    ROOTMNT}
export ROOTMNT=/a
udevadm control --exit
mount -n --move /dev  /a/dev
mount -n --move /run  ${
    
    ROOTMNT}/run
mount -n --move /proc ${
    
    ROOTMNT}/proc
mount -n --move /sys  ${
    
    ROOTMNT}/sys
switch_root ${
    
    ROOTMNT} /sbin/init


#!/bin/bash
mount -o remount,rw /dev/sda1 /
udevd --daemon
udevadm trigger --action=add
udevadm settle

export HOME=/root
exec /bin/bash -l



mkdir /mnt/sysroot
mount /dev/sda1  /mnt/sysroot
echo "The pid now is $$"  
exec switch_root  /mnt/sysroot /sbin/init



mount -t ext4 /dev/sda1 /mnt/sysroot
mount -n --move /sys /mnt/sysroot/sys
mount --move /proc /mnt/sysroot/proc
mount --move /dev /mnt/sysroot/dev
exec switch_root -c /dev/console /mnt/sysroot /sbin/init
chroot	/mnt/sysroot	/bin/bash

echo "Mount real rootfs to /mnt/..."
mount -t ext4 /dev/sda1 /mnt  #挂载真正的文件系统
#echo "Switch to read rootfs..."
exec switch_root /mnt /sbin/init   #切换到真正的文件系统

sleep 10
mkdir /mnt/lower /mnt/upper /mnt/work /mnt/sysroot
/bin/mount -t squashfs /media/sr0/casper/filesystem.squashfs /mnt/lower -o loop
/bin/mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/upper,workdir=/mnt/work overlay /mnt/sysroot
echo "The pid now is $$"                   //打印当前pid 确保为1
exec switch_root  /mnt/sysroot /sbin/init             //转换

注意:本次制作的根分区是在硬盘的sda1分区

2.4 启动参数设置

1、linux挂载分区时所依赖的信息存放于根文件系统/etc/fstab这个文件中

cat /etc/fstab
/dev/sda1 / ext4 defaults 0 1

本人博客:/etc/fstab详解

2、启动参数设置

setenv bootargs console=ttyS0,115200  root=/dev/sda1  rw  init=/init

本人博客:uboot启动详解

三、硬盘启动验证

在linux启动完成后可以通过:

mount |grep sd  
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
//说明此时已经从sda1启动

四、从硬盘启动失败总结

本人在完成这个项目过程中也是费了很长时间从ramfs到硬盘根文件系统的过渡,分析主要原因如下:
1、busybox中init文件代码存在问题,后面采用的方法是直接在uboot启动的ramfs中直接生成img,然后手动解开img,再将里面的init替换成Image.gz中的init:

mkinitramfs -o test.img 5.10.0
mkdir a
cd a;cpio -idvm <../test.img

本人博客:mkinitrd命令详解(注意部分系统为mkinitramfs)

本人博客:cpio格式手动解开命令

2、启动参数中需要加init=/init;
3、硬盘/etc/fstab中要加入自动挂载参数:/dev/sda1 / ext4 defaults 0 1

附mkinitramfs生成的img中init文件如下

#!/bin/bash

# Default PATH differs between shells, and is not automatically exported
# by klibc dash.  Make it consistent.
export PATH=/sbin:/usr/sbin:/bin:/usr/bin

[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc

case " $(cat /proc/cmdline) " in
*\ quiet\ *)
	quiet=y
	;;
*)
	quiet=n
	echo "Loading, please wait..."
	;;
esac
export quiet

# Note that this only becomes /dev on the real filesystem if udev's scripts
# are used; which they will be, but it's worth pointing out
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
mkdir -m 0755 /run/initramfs

# Export the dpkg architecture
export DPKG_ARCH=
. /conf/arch.conf

# Set modprobe env
export MODPROBE_OPTIONS="-qb"

# Export relevant variables
export ROOT=
export ROOTDELAY=
export ROOTFLAGS=
export ROOTFSTYPE=
export IP=
export BOOT=
export BOOTIF=
export UBIMTD=
export break=
export init=/sbin/init
export readonly=y
export rootmnt=/root
export debug=
export panic=
export blacklist=
export resume=
export resume_offset=
export drop_caps=
export fastboot=n
export forcefsck=n
export fsckfix=


# Bring in the main config
. /conf/initramfs.conf
for conf in conf/conf.d/*; do
	[ -f ${conf} ] && . ${conf}
done
. /scripts/functions

# Parse command line options
for x in $(cat /proc/cmdline); do
	case $x in
	init=*)
		init=${x#init=}
		;;
	root=*)
		ROOT=${x#root=}
		if [ -z "${BOOT}" ] && [ "$ROOT" = "/dev/nfs" ]; then
			BOOT=nfs
		fi
                ;;
	rootflags=*)
		ROOTFLAGS="-o ${x#rootflags=}"
		;;
	rootfstype=*)
		ROOTFSTYPE="${x#rootfstype=}"
		;;
	rootdelay=*)
		ROOTDELAY="${x#rootdelay=}"
		case ${ROOTDELAY} in
		*[![:digit:].]*)
			ROOTDELAY=
			;;
		esac
		;;
	nfsroot=*)
		NFSROOT="${x#nfsroot=}"
		;;
	ip=*)
		IP="${x#ip=}"
		;;
	boot=*)
		BOOT=${x#boot=}
		;;
	ubi.mtd=*)
		UBIMTD=${x#ubi.mtd=}
		;;
	resume=*)
		RESUME="${x#resume=}"
		case $RESUME in
	        UUID=*)
			RESUME="/dev/disk/by-uuid/${RESUME#UUID=}"
		esac
		;;
	resume_offset=*)
		resume_offset="${x#resume_offset=}"
		;;
	noresume)
		noresume=y
		;;
	drop_capabilities=*)
		drop_caps="-d ${x#drop_capabilities=}"
		;;
	panic=*)
		panic="${x#panic=}"
		case ${panic} in
		*[![:digit:].]*)
			panic=
			;;
		esac
		;;
	ro)
		readonly=y
		;;
	rw)
		readonly=n
		;;
	debug)
		debug=y
		quiet=n
		if [ -n "${netconsole}" ]; then
			exec >/dev/kmsg 2>&1
		else
			exec >/run/initramfs/initramfs.debug 2>&1
		fi
		set -x
		;;
	debug=*)
		debug=y
		quiet=n
		set -x
		;;
	break=*)
		break=${x#break=}
		;;
	break)
		break=premount
		;;
	blacklist=*)
		blacklist=${x#blacklist=}
		;;
	netconsole=*)
		netconsole=${x#netconsole=}
		[ "x$debug" = "xy" ] && exec >/dev/kmsg 2>&1
		;;
	BOOTIF=*)
		BOOTIF=${x#BOOTIF=}
		;;
	fastboot|fsck.mode=skip)
		fastboot=y
		;;
	forcefsck|fsck.mode=force)
		forcefsck=y
		;;
	fsckfix|fsck.repair=yes)
		fsckfix=y
		;;
	fsck.repair=no)
		fsckfix=n
		;;
	esac
done

# Default to BOOT=local if no boot script defined.
if [ -z "${BOOT}" ]; then
	BOOT=local
fi

if [ -n "${noresume}" ] || [ "$RESUME" = none ]; then
	export noresume=y
	unset resume
else
	resume=${RESUME:-}
fi

maybe_break top

# Don't do log messages here to avoid confusing graphical boots
run_scripts /scripts/init-top

maybe_break modules
[ "$quiet" != "y" ] && log_begin_msg "Loading essential drivers"
[ -n "${netconsole}" ] && modprobe netconsole netconsole="${netconsole}"
load_modules
[ "$quiet" != "y" ] && log_end_msg

if [ "$ROOTDELAY" ]; then
	sleep $ROOTDELAY
fi

maybe_break premount
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"
run_scripts /scripts/init-premount
[ "$quiet" != "y" ] && log_end_msg

maybe_break mount
log_begin_msg "Mounting root file system"
# Always load local and nfs (since these might be needed for /etc or
# /usr, irrespective of the boot script used to mount the rootfs).
. /scripts/local
. /scripts/nfs
. /scripts/${BOOT}
parse_numeric ${ROOT}
maybe_break mountroot
mount_top
mount_premount
mountroot
log_end_msg

if read_fstab_entry /usr; then
	log_begin_msg "Mounting /usr file system"
	mountfs /usr
	log_end_msg
fi

# Mount cleanup
mount_bottom
nfs_bottom
local_bottom

maybe_break bottom
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
# We expect udev's init-bottom script to move /dev to ${rootmnt}/dev
run_scripts /scripts/init-bottom
[ "$quiet" != "y" ] && log_end_msg

# Move /run to the root
mount -n -o move /run ${rootmnt}/run

validate_init() {
	run-init -n "${rootmnt}" "${1}"
}

# Check init is really there
if ! validate_init "$init"; then
	echo "Target filesystem doesn't have requested ${init}."
	init=
	for inittest in /sbin/init /etc/init /bin/init /bin/sh; do
		if validate_init "${inittest}"; then
			init="$inittest"
			break
		fi
	done
fi

# No init on rootmount
if ! validate_init "${init}" ; then
	panic "No init found. Try passing init= bootarg."
fi

maybe_break init

# don't leak too much of env - some init(8) don't clear it
# (keep init, rootmnt, drop_caps)
unset debug
unset MODPROBE_OPTIONS
unset DPKG_ARCH
unset ROOTFLAGS
unset ROOTFSTYPE
unset ROOTDELAY
unset ROOT
unset IP
unset BOOT
unset BOOTIF
unset UBIMTD
unset blacklist
unset break
unset noresume
unset panic
unset quiet
unset readonly
unset resume
unset resume_offset
unset fastboot
unset forcefsck
unset fsckfix

# Move virtual filesystems over to the real filesystem
mount -n -o move /sys ${rootmnt}/sys
mount -n -o move /proc ${rootmnt}/proc

# Chain to real filesystem
exec run-init ${drop_caps} ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1
echo "Something went badly wrong in the initramfs."
panic "Please file a bug on initramfs-tools."

五、本人其他博客链接

1、手动分区和打包根文件系统

2、/etc/fstab详解

3、uboot启动详解

4、mkinitrd命令详解(注意部分系统为mkinitramfs)

5、cpio格式手动解开命令

6、grub.cfg启动详解

六、其他优化项

1、测试阶段内核Image.gz和设备数dtb都是用tftp启动,项目完成后将文件写至nand中启动;
2、硬盘采用UUID的方式启动,适用于多块硬盘连接到机器上无法固定启动硬盘号的情况;

猜你喜欢

转载自blog.csdn.net/Luckiers/article/details/125960664