- 熟练掌握 Linux 操作系统的使用
- 了解 Linux 操作系统的运作过程,理解内核与外围支撑系统的关系
- 通过实验定制 Linux 系统内核与外围支撑系统,加深对于开源操作系统的认识
- 实现具有特色功能的自启动小系统
- 实现最新版本 Linux kernel 及其配套 RAMDisk 文件系统的定制
- 要求:kernel < 4 MB,initrd.img < 24 MB
- 功能:
- 通过 U 盘加载 kernel 和 img 启动
- 支持多用户登录(console 界面和 ssh 网络方式)
- 系统支持通过 ssh 方式访问其他机器
- 可挂载 U 盘
- 可访问机器上的 windows 分区(ntfs-3gfs 支持)
实验基于 Ubuntu 21.04、Linux Kernel 5.14.0、VMWare Workstation Pro 进行
initrd 提供了以下功能:
- 提供一个临时的根文件系统
- 加载必要驱动(以访问真实的根文件系统)
- 挂载根文件系统
- 根切换
- 编辑
/etc/default/grub
,关闭默认的hidden
行为。 - 编辑
/boot/grub/grub.cfg
,为定制的 initrd 添加一个新的 Menu Entry。
通过 tools/add_cmd.sh
将命令及其依赖添加到当前目录下
# Add commands with the dependencies required
# to the current directory as root.
addto_wd() {
WORK_DIR=$(pwd)
root=${1%/*}
file=${1##*/}
if [[ ! -d $WORK_DIR$1 ]]; then
if [[ ! -d $WORK_DIR$root ]]; then
mkdir -p $WORK_DIR$root
fi
cp $1 $WORK_DIR$1
fi
}
for arg in $@; do
if [[ $arg =~ "/" ]]; then # is path
path=$arg
else # is command
path=$(which $arg)
fi
echo $path
addto_wd $path
# ldd $path
for line in $(ldd $path); do
if [[ ${line:0:1} == '/' ]]; then
echo $line
addto_wd $line
fi
done
done
将 bash 命令及其依赖添加到定制 initrd 中,创建 init
文件如下:
#!/bin/bash
export PATH=/usr/sbin:/usr/bin:/bin
bash
init
文件需要给予执行权限,否则报错:no working init,进而 kernel panic。
$ chmod 755 init
重新打包 initramfs
$ find . -path ./tools -prune -o -print | cpio -o -H newc | gzip > /boot/initrd.img-5.11.0-25-modified
通过 tools/add_mod.sh
将模块及其依赖添加到当前目录下
# Add kernel modules with dependencies
# to the current directory as root.
addto_wd() {
WORK_DIR=$(pwd)
root=${1%/*}
file=${1##*/}
if [[ ! -d $WORK_DIR$1 ]]; then
if [[ ! -d $WORK_DIR$root ]]; then
mkdir -p $WORK_DIR$root
fi
cp $1 $WORK_DIR$1
fi
}
addmod() {
fileline=$(modinfo $1 | grep filename)
path=${fileline##* }
if [[ ! -e $(pwd)$path ]]; then
echo $1": "$path
addto_wd $path
fi
}
addmod_withdeps() {
depsline=$(modinfo $1 | grep depends)
depends=${depsline##* }
for dep in ${depends//,/ }; do
addmod_withdeps $dep
done
addmod $1
}
for mod in $@; do
addmod_withdeps $mod
done
通过 tools/lsmodinfo.sh
以更便捷的方式显示模块的必要信息
# List infomation about all kernel modules
# with its filename, description and depends.
lsmod | while read line; do
# echo $line
if [[ "${line:0:6}" != "Module" ]]; then
echo $line
module=${line%% *}
# echo $module
echo " "$(modinfo $module | grep filename)
echo " "$(modinfo $module | grep description)
echo
fi
done
由于 VMware 给虚拟机提供了虚拟的 scsi 硬盘,为使能挂载该硬盘,只需添加 mptspi
模块及其依赖。添加 insmod
、mkdir
、mount
等命令,并编辑 init
如下:
#!/bin/bash
export PATH=/usr/sbin:/usr/bin:/bin
insmod /lib/modules/5.11.0-25-generic/kernel/drivers/message/fusion/mptbase.ko
insmod /lib/modules/5.11.0-25-generic/kernel/drivers/message/fusion/mptscsih.ko
insmod /lib/modules/5.11.0-25-generic/kernel/drivers/scsi/scsi_transport_spi.ko
insmod /lib/modules/5.11.0-25-generic/kernel/drivers/message/fusion/mptspi.ko
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc
mknod /dev/sda3 b 8 3 # Look for major and minor numbers by `ls -l /dev/sdax`
mkfs -t ext4 /dev/sda3
mount /dev/sda3 /root
bash
依赖 sysfs 文件系统,挂载 /sys
;规则位于 /lib/udev
;配置位于 /etc/udev
;并在 /dev
目录下创建设备节点。
-
从解压的原始 initrd 中拷贝
rule.d
与/lib/modules/xxx-xx-generic/modules.*
到小系统 initrd 中,后者包含了udevadm
自动添加时所必要的规则等,否则无法自动管理设备。 -
通过追踪原始 initrd 的执行,发现其调用了
/scripts
下的脚本以调用udevadm
,最终定位到scripts/init-top/udev
中的以下几行命令
udevadm trigger --type=subsystems --action=add
udevadm trigger --type=devices --action=add
udevadm settle || true
其中 udevadm settle
等待所有 udev
事件执行完毕,否则由于之后的 mount
命令异步执行,还未完成所有模块的加载便试图挂载硬盘,因此此处需要进行阻塞。
-
此外,原始 initrd 中
/lib/systemd
是/bin/udevadm
的软链接,确保链接的路径正确:systemd-udevd -> ../../bin/udevadm
,而非bin/udevadm
。 -
编辑
init
文件如下:
#!/bin/bash
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /run ] || mkdir /run # for udevd to create /run/udev
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc
# Mount udev
mount -t devtmpfs -o $dev_exec,nosuid,mode=0755 udev /dev
log_level=info
SYSTEMD_LOG_LEVEL=$log_level /lib/systemd/systemd-udevd --daemon --resolve-names=never
udevadm trigger --type=subsystems --action=add
udevadm trigger --type=devices --action=add
udevadm settle || true
mount /dev/sda3 /root
bash
- 认证体系 PAM 配置位于
/etc/pam.d
目录下,依赖/lib/security
。 - 可以
chroot
到小系统 initrd 根目录下,通过strace login
查看缺少的文件。- 添加
/etc/shadow
、/etc/passwd
、/etc/pam.d
、/lib/x86_64-linux-gnu/security
- 报错 Login incorrect:添加
/etc/nsswitch.conf
、/lib/x86_64-linux-gnu/libnss_*
- 报错 Error in service module:
/etc/login.defs
、/etc/security
- 添加
systemd
必须以 PID 1 启动,因此应在 init
文件末尾执行 exec /sbin/init
将当前执行 init
的进程切换到执行 systemd
,否则报错 kernel panic。没有查找到任何关于 systemd
的功能机制的文档或说明,因此所有需要添加的命令与文件全靠不断试错,此处应该可以查看 systemd
源码分析。
通过反复尝试,仅需要添加 agetty
一个命令即可,并且需要拷贝 /lib/systemd
到小系统目录下。在此基础上可以通过 man systemd.special
查看 systemd
各单元的描述对 /lib/systemd
进行进一步的裁剪,建议一开始装 Linux 时就应安装无图形化界面版本,此处就可以省去裁剪 gnome
、gdm
、gvfs
等相关服务配置文件的操作。
添加以下网络配置相关命令 ifconfig
、ping
、ip
、ssh
、sshd
等,以及 VMWare 抽象的 Intel e1000 网卡的相应驱动。
$ bash tools/addcmd.sh ifconfig ping dhclient ip ssh sshd
$ bash tools/addmod.sh e1000
$ cp /etc/ssh etc -r
$ sudo cp /etc/ssh/*_key etc/ssh/
查看网络接口信息
$ ip link show
开启网络适配器
$ ip link set eth0 up
网络适配器相应固件 /lib/firmware/xxx
可能也需要添加到 initrd 中,否则可能会有相应报错。
配置网络适配器地址
$ ip addr add 192.168.0.99/24 dev eth0
配置默认路由
$ ip route add default via 192.168.0.1 dev eth0
编辑 /etc/sshd_config
:PermitRootLogin yes
.
若要通过 ssh
连接到当前系统,需要手动执行绝对路径启动 sshd
以监听客户端的连接。此处应该可以通过添加到 systemd
的 target 中自动启动。
$ /sbin/init
查看 terminfo
描述
$ infocmp
# Reconstructed via infocmp from file: /lib/terminfo/x/xterm-256color
xterm-256color | xterm with 256 colors
拷贝相应的 terminfo
文件,并且需要将 $TERM
变量设置为相应名称。为了使新建终端会话中自动设定以上变量,编辑 /etc/profile
如下:
export PATH=/usr/sbin:/sbin:$PATH
export TERM=xterm-256color
# generated by ASCII Generator
echo -e \
" \033[31m__ ___ \033[0m\033[32m_\033[0m \033[33m_\033[0m ____ _____\n" \
" \033[31m/ |/ /\033[0m\033[32m(_)\033[0m\033[34m____\033[0m \033[33m(_)\033[0m/ __ \/ ___/\n" \
" \033[31m/ /|_/ /\033[0m\033[32m/ /\033[0m\033[34m/ __ \ \033[0m\033[33m/ /\033[0m/ / / /\__ \ \n" \
" \033[31m/ / / /\033[0m\033[32m/ /\033[0m\033[34m/ / / /\033[0m\033[33m/ /\033[0m/ /_/ /___/ / \n" \
"\033[31m/_/ /_/\033[0m\033[32m/_/\033[0m\033[34m/_/ /_/\033[0m\033[33m/_/\033[0m \____//____/ \n"
添加命令及其依赖到当前目录作为根的文件系统中
$ bash tools/addcmd.sh COMMAND...
添加模块及其依赖到当前目录作为根的文件系统中
$ bash tools/addmod.sh MODULE...
从当前目录中重新打包 initrd 镜像
$ bash tools/repack_initrd.sh
相比于 lsmod
与 modinfo
,以更便捷的方式显示当前 Linux Kernel 中所有模块的必要信息
$ bash tools/lsmodinfo.sh
- 通过
make menuconfig
在图形化 menu 中进行配置 systemd
所需内核项可在其 repo https://github.com/systemd/systemd/blob/main/README 中找到。- menuconfig 中可输入
/
查找配置变量名,并输入1
跳转到对应项。
- General setup
- Kernel compression mode (XZ)
- Control Group support
- Support for eBPF programs attached to cgroups
- Initial RAM filesystem and RAM disk (initramfs/initrd) support:选中 initrd 对应格式
- Power management and ACPI options
- ACPI (Advanced Configuration and Power Interface) Support:若不选中
poweroff
或者reboot
后机器无法断电
- ACPI (Advanced Configuration and Power Interface) Support:若不选中
- General architecture-dependent options
- Enable seccomp to safely execute untrusted bytecode
- Executable file formats
- Kernel support for ELF binaries
- Write ELF core dumps with partial segments
- Kernel support for scripts starting with #!
- Networking support
- Networking options
- TCP/IP networking
- Networking options
- Device Drivers
- Serial ATA and Parallel ATA drivers (libata)
- Network device support
- Ethernet driver support:选中设备网络适配器对应品牌
- Input device support
- Keyboards
- AT keyboard:指 PS/2 接口键盘以及笔记本自带的键盘,不包括 USB 键盘
- Keyboards
- HID support
- Generic HID driver:包括 USB 键盘
- USB support
- xHCI HCD (USB 3.0) support
- EHCI HCD (USB 2.0) support
- OHCI HCD (USB 1.1) support
- USB Mass Storage support
- File systems
- The Extended 4 (ext4) filesystem
- Inotify support for userspace
systemd
必需 - Native language support:支持挂载硬盘的不同格式所需
- Codepage 437 (United States, Canada)
- Simplified Chinese charset (CP936, GB2312)
- ASCII (United States)
- NLS ISO 8859-1 (Latin 1; Western European Languages)
- NLS UTF-8
- Security options:全关
- Cryptographic API:基本只需保留
systemd
requirements 中的项 - Library routines:能关的全关
- Kernel hacking:全关
安装所需 package
$ sudo su
# apt update && apt upgrade
# apt install git bison libopts25 libselinux1-dev m4 help2man libopts25-dev flex libfont-freetype-perl automake make autotools-dev autopoint libfreetype6-dev texinfo python autogen autoconf libtool libfuse3-3 unifont gettext binutils pkg-config liblzma5 libdevmapper-dev
Bootstrap GRUB
# git clone git://git.savannah.gnu.org/grub.git
# cd grub
# ./bootstrap
编译 GRUB (64 bit UEFI)
# mkdir efi64
# cd efi64
# ../configure --target=x86_64 --with-platform=efi && make
挂载 U 盘
# fdisk -l
# mkdir /mnt/usb
# mount /dev/sdb1 /mnt/usb
安装 GRUB
# cd ../efi64/grub-core
# grub-install -d $PWD --force --removable --no-floppy --target=x86_64-efi --boot-directory=/mnt/usb/boot --efi-directory=/mnt/usb
创建 boot/grub/grub.cfg
如下:
function recordfail {
set recordfail=1
if [ -n "${have_grubenv}" ]; then if [ -z "${boot_once}" ]; then save_env recordfail; fi; fi
}
function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}
function gfxmode {
set gfxpayload="${1}"
if [ "${1}" = "keep" ]; then
set vt_handoff=vt.handoff=7
else
set vt_handoff=
fi
}
menuentry 'Minimal OS' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-5949ae40-f727-42f6-a4a6-751c22b8b54d' {
recordfail
load_video
gfxmode $linux_gfx_mode
insmod gzio
insmod part_gpt
insmod ext2
echo 'loading minimal OS ...'
linux /boot/vmlinuz-5.14.0-minimal ro text
echo 'loading initrd ...'
initrd /boot/initrd.img-minimal
}
拷贝定制的小系统 initrd 镜像以及编译的 Linux 内核到 grub.cfg
中指定的位置,即可成功从 U 盘中启动定制小系统。🎉