最近有一块开发板需要制作一个定制的Linux操作系统,通过该项目总结一下相关的资料,包含rootfs切换到真正的根文件系统、如何制作根文件系统和相关的启动配置
机器简介:基于ARM架构的A53开发板,集成了本公司自研主控芯片,采用uboot引导,系统安装在ssd上,内核从nand flash启动;
1、busybox制作精简的文件系统并完成ramfs启动;
2、通过精简的文件系统制作完整的文件系统并拷贝到硬盘;
3、rootfs切换到真正的根文件配置;
4、启动参数设置;
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的形式挂载在内存中;
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
本人博客:分区和打包根文件系统
可以直接在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分区
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的方式启动,适用于多块硬盘连接到机器上无法固定启动硬盘号的情况;