smart-os/02_build_img.sh
2022-10-27 21:06:23 +08:00

488 lines
17 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
#----------------------------------------------
#
# 导入公共环境
#
#----------------------------------------------
. ./common.sh
#----------------------------------------------
#
# 进行目录瘦身
#
#----------------------------------------------
./mk_strip.sh
#----------------------------------------------
#
# 制作磁盘
#
#----------------------------------------------
echo "${CYAN}--- build disk --- ${NC}"
# 创建磁盘 128M 或 256M
if [ "${with_gcc}" = false ]; then
create_disk disk.img 4096
else
create_disk disk.img 4096
fi
echo "${GREEN}+++ build disk ok +++${NC}"
# 磁盘镜像挂载到具体设备
loop_dev=$(losetup -f)
# fdisk -l disk.img 查看 start 为 2048, unit 512 所以 -o 偏移扇区 1048576 = 2048 x 512
losetup -o 1048576 ${loop_dev} disk.img
# 对磁盘进行格式化
mkfs.ext3 ${loop_dev}
# 如果制作的 disk.img
diskfs="diskfs"
# 挂载磁盘到本地目录
mkdir -pv ${diskfs}
mount -t ext3 ${loop_dev} ${diskfs}
# 安装grub 引导
grub-install --boot-directory=${diskfs}/boot/ --target=i386-pc --modules=part_msdos disk.img
#---------------------------------------------
#
# 制作内核和 rootfs ( run 目录下 udev 被服务 udevd 使用,否则 xfce 鼠标不能使用 )
#
#---------------------------------------------
rm -rf rootfs
mkdir -pv rootfs
mkdir -pv rootfs/dev
mkdir -pv rootfs/etc
mkdir -pv rootfs/sys
mkdir -pv rootfs/mnt
mkdir -pv rootfs/run
mkdir -pv rootfs/tmp
mkdir -pv rootfs/lib
mkdir -pv rootfs/sbin
mkdir -pv rootfs/proc
mkdir -pv rootfs/root
mkdir -pv rootfs/lib64
mkdir -pv rootfs/var/run
mkdir -pv rootfs/lib/modules
# 拷贝内核镜像
cp ${linux_install}/bzImage ${diskfs}/boot/bzImage
cp ${linux_install}/lib ${diskfs}/ -r
# 拷贝 glibc 到 rootfs
cp ${glibc_install}/* rootfs/ -r
rm -rf rootfs/var/db
rm -rf rootfs/share
rm -rf rootfs/usr/share
find rootfs/ -name "*.a" -exec rm -rf {} \;
# 编译的镜像带有 gcc 编译器
if [ "${with_gcc}" = false ]; then
rm -rf rootfs/usr/include
else
cp ${glibc_install}/usr/lib64/libc_nonshared.a rootfs/usr/lib64
fi
#----------------------------------------------------------------------
# 这个解释器必须设置对,否则系统会启动时 crash, 导致启动失败 !!!!!!
# 这个现在 glibc 编译时,已经自动生成,先注释掉
#-----------------------------------------------------------------------
# ln -s /lib/ld-2.32.so rootfs/lib64/ld-linux-x86-64.so.2
# 拷贝 busybox 到 rootfs
cp ${busybox_install}/* rootfs/ -r
#-----------------------------------------------
#
# 制作启动文件系统 initramfs
#
#-----------------------------------------------
cd rootfs
echo "${CYAN}--- build initrd ---${NC}"
# 这种方法也可以 mkinitramfs -k -o ./${diskfs}/boot/initrd 4.14.9
# 利用 Busybox 采用脚本制作 init 脚本 https://blog.csdn.net/embeddedman/article/details/7721926
# 光驱挂载 : /dev/cdrom 是 /dev/sr0 的软连接,也就是说 /dev/sr0 才是实际意义上的光驱。所以没有软连接,
# 照样可以挂载光驱。使用命令"mount /dev/sr0 /mnt/cdrom"便可以实现挂载。
make_init() {
cat<<"EOF">init
#!/bin/sh
# 必须首先挂载,否则 mdev 不能正常工作
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mount -t devtmpfs udev /dev
mount -t tmpfs tmpfs /tmp -o mode=1777
# 必须挂载一下,否则下面的 mount 不上
mdev -s
mount -t ext3 /dev/sda1 /mnt
# 关闭内核烦人的输出信息
echo 0 > /proc/sys/kernel/printk
# 热插拔处理都交给 mdev
# echo /sbin/mdev > /proc/sys/kernel/hotplug
echo -e "\n\e[0;32mBoot took $(cut -d' ' -f1 /proc/uptime) seconds\e[0m"
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
# 切换之前,修改 mount 路径
mount --move /dev /mnt/dev
mount --move /sys /mnt/sys
mount --move /proc /mnt/proc
mount --move /tmp /mnt/tmp
# 因为 busybox 的 init 会重置环境变量 PATH 等,因此需要放到这里加载
export LD_LIBRARY_PATH="/lib:/lib64:/usr/lib:/usr/lib64:/usr/local/lib:/usr/local/lib64:/usr/lib/x86_64-linux-gnu"
# 切换到真正的磁盘系统上 rootfs ---> diskfs 切换到真正的文件系统
exec switch_root /mnt /sbin/init
EOF
# /sbin/init [switch_root 执行] ---> /etc/inittab [定义了启动顺序] --->
# /etc/init.d/rcS [系统 mount, 安装驱动,配置网络] --->
# /etc/init.d/rc.local [文件配置应用程序需要的环境变量] ---> /etc/profile [部分初始化]
chmod +x init
}
make_init
# 下面这些不用了,利用脚本里面的 busybox 的 mdev -s 自动挂载
# mknod -m 644 dev/tty0 c 4 1
# mknod -m 644 dev/tty c 5 0
# mknod -m 600 dev/console c 5 1
# mknod -m 644 dev/null c 1 3
# mknod -m 640 dev/sda1 b 8 1
# 指定了利用 /etc/init.d/rcS 启动
cat<<"EOF">etc/inittab
# 启动 syslogd ( 日志系统 )
::sysinit:/bin/echo "starting syslogd ... ..."
::sysinit:/sbin/syslogd
::sysinit:/sbin/klogd
# 启动 udevd 服务,保证鼠标设备能正常监视,否则桌面系统下键盘不能使用
::sysinit:/bin/echo "starting udevd ... ..."
::sysinit:/usr/sbin/udevd --daemon
::sysinit:/usr/sbin/udevadm trigger
::sysinit:/usr/sbin/udevadm settle
# 基本启动信息都放到这个脚本里
::sysinit:echo "sysinit ++++++++++++++++++++++++++++++++++++++"
::sysinit:/etc/init.d/rcS
::sysinit:echo "sysinit ++++++++++++++++++++++++++++++++++++++"
# /bin/sh invocations on selected ttys
#
# Note below that we prefix the shell commands with a "-" to indicate to the
# shell that it is supposed to be a login shell. Normally this is handled by
# login, but since we are bypassing login in this case, BusyBox lets you do
# this yourself...
#
# Start an "askfirst" shell on the console (whatever that may be) -f root 自动登录
# 一定要加 tty1 ,否则登录时,会提示 : root login on 'UNKNOWN'
tty1::respawn:-/bin/login -f root
# Start an "askfirst" shell on /dev/tty2-4
tty2::respawn:-/bin/sh
tty3::respawn:-/bin/sh
tty4::respawn:-/bin/sh
# /sbin/getty invocations for selected ttys
tty4::respawn:/sbin/getty 38400 tty5
tty5::respawn:/sbin/getty 38400 tty6
# Example of how to put a getty on a serial line (for a terminal)
#::respawn:/sbin/getty -L ttyS0 9600 vt100
#::respawn:/sbin/getty -L ttyS1 9600 vt100
#
# Example how to put a getty on a modem line.
#::respawn:/sbin/getty 57600 ttyS2
# Stuff to do when restarting the init process
::restart:/sbin/init
# Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
EOF
# 制作 initrd 文件系统
find . | cpio -R root:root -H newc -o | gzip -9 > ../${diskfs}/boot/initrd
echo "${GREEN}+++ build initrd ok +++${NC}"
cd ..
#--------------------------------------------------------------
#
# 生成磁盘文件系统(利用 busybox 结构,省的自己创建了)
#
#--------------------------------------------------------------
echo "${CYAN}--- build diskfs ---${NC}"
cp rootfs/* ${diskfs} -r
# +++ 通用工具 +++
if [ "${with_util}" = true ]; then
echo "${RED} ... build with-util${NC}"
# 单独的 lshw
cp ${lshw_install}/* ${diskfs} -r
# 单独的 lsof
cp ${lsof_install}/* ${diskfs} -r
# 单独的 pciutils
cp ${pciutils_install}/* ${diskfs} -r
if [ -f "${diskfs}/usr/share/pci.ids.gz" ]; then
mkdir -pv ${diskfs}/usr/local/share
mv ${diskfs}/usr/share/pci.ids.gz ${diskfs}/usr/local/share/pci.ids.gz
fi
# 单独的 strace
cp ${strace_install}/* ${diskfs} -r
fi
# +++ ufw +++
if [ "${with_ufw}" = true ]; then
echo "${RED} ... build with-ufw${NC}"
# 拷贝 libmnl
cp ${libmnl_install}/* ${diskfs} -r
# 拷贝 libnftnl
cp ${libnftnl_install}/* ${diskfs} -r
# 拷贝 iptables
cp ${iptables_install}/* ${diskfs} -r
fi
# +++ openssh +++
if [ "${with_ssh}" = true ]; then
echo "${RED} ... build with-ssh${NC}"
# 带有 openssl
cp ${openssl_install}/* ${diskfs} -r
# 带有 openssh
cp ${openssh_install}/* ${diskfs} -r
fi
# +++ gcc +++
if [ "${with_gcc}" = true ]; then
echo "${RED} ... build with-gcc${NC}"
cp ${gcc_install}/* ${diskfs} -r
cp ${binutils_install}/usr/x86_64-pc-linux-gnu/* ${diskfs} -r
fi
rm -rf ${diskfs}/init ${diskfs}/lost+found
# 测试用户登陆模式: root/123456
if [ "${with_login}" = true ]; then
echo "${RED} ... build with-login${NC}"
./mk_login.sh ${diskfs}
fi
# +++ xfce desktop +++
if [ "${with_xfce}" = true ]; then
echo "${RED} ... build xfce desktop${NC}"
# 构建 Xorg 的键盘数据
rm ${xfce_install}/usr/local/share/X11/xkb -rf
ln -s /usr/share/X11/xkb ${xfce_install}/usr/local/share/X11
# 依赖版本 libpcre.so.3
if [ -f "${xfce_install}/usr/local/lib/libpcre.so.1" ]; then
cp ${xfce_install}/usr/local/lib/libpcre.so.1 ${xfce_install}/usr/local/lib/libpcre.so.3
fi
# 依赖版本 libedit2
if [ -f "${xfce_install}/usr/local/lib/libedit.so.0" ]; then
cp ${xfce_install}/usr/local/lib/libedit.so.0 ${xfce_install}/usr/local/lib/libedit.so.2
fi
# 依赖版本 libtinfo.so.5
if [ -f "${xfce_install}/usr/lib/libtinfo.so.6" ]; then
cp ${xfce_install}/usr/lib/libtinfo.so.6 ${xfce_install}/usr/lib/libtinfo.so.5
fi
# 依赖版本 libffi.so.6
if [ -f "${xfce_install}/usr/local/lib/libffi.so.8" ]; then
cp ${xfce_install}/usr/local/lib/libffi.so.8 ${xfce_install}/usr/local/lib/libffi.so.6
fi
# dbus 用户添加
echo "video:x:44:" >> ${diskfs}/etc/group
echo "messagebus:x:107:" >> ${diskfs}/etc/group
echo "messagebus:x:103:107::/nonexistent:/usr/sbin/nologin" >> ${diskfs}/etc/passwd
# dbus 启动需要这个,否则 upowerd 就不能正常工作
cp ${xfce_install}/usr/share/dbus-1/* ${xfce_install}/usr/local/share/dbus-1/ -r
# dbus 启动脚本
# dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
# dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
# dbus-daemon --config-file=/usr/share/defaults/at-spi2/accessibility.conf --nofork --print-address 3
# 常用的 dbus 命令
# 列出所有的dbus服务 :
# dbus-send --system --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListActivatableNames
# UPower 的dbus服务 :
# dbus-send --print-reply --system --dest=org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.EnumerateDevices
# 产生 xfce 启动脚本,进入系统后,手工执行这个就能拉起桌面
echo "if [ -f "/swapfile" ]; then" > ${diskfs}/xfce.sh
echo " dd if=/dev/zero of=/swapfile bs=1M count=2048" >> ${diskfs}/xfce.sh
echo " mkswap /swapfile" >> ${diskfs}/xfce.sh
echo "fi" >> ${diskfs}/xfce.sh
echo "swapon /swapfile" >> ${diskfs}/xfce.sh
echo "/usr/libexec/upowerd &" >> ${diskfs}/xinitrc
echo "/usr/local/bin/xfce4-session" >> ${diskfs}/xinitrc
echo "dbus-daemon --system --nopidfile --systemd-activation" >> ${diskfs}/xfce.sh
echo "xinit /xinitrc -- /usr/local/bin/Xorg :10" >> ${diskfs}/xfce.sh
chmod +x ${diskfs}/xfce.sh ${diskfs}/xinitrc
# 添加 machine-id
mkdir -p ${diskfs}/usr/local/var/lib/dbus
echo "2add25d2f5994832ba171755bc21f9fe" > ${diskfs}/etc/machine-id
echo "2add25d2f5994832ba171755bc21f9fe" > ${diskfs}/usr/local/var/lib/dbus/machine-id
# 这些本来需要编译完成,目前暂且拷贝
# cp /usr/lib/x86_64-linux-gnu/libLLVM-10.so.1 build/xfce_install/usr/lib/x86_64-linux-gnu/
# 拷贝 xfce4 到镜像目录,删除 .a 文件减少体积,其实编译选型不编译文档和测试代码会更小
find ${xfce_install}/ -name "*.a" -exec rm -rf {} \;
find ${xfce_install}/ -name "man" -exec rm -rf {} \;
find ${xfce_install}/ -name "*doc" -exec rm -rf {} \;
cp ${xfce_install}/* ${diskfs} -r -n
# 删除冗余文件,防止后续编译很多警告
# 依赖版本 libpcre.so.3
if [ -f "${xfce_install}/usr/local/lib/libpcre.so.1" ]; then
rm ${xfce_install}/usr/local/lib/libpcre.so.3 -rf
fi
# 依赖版本 libedit2
if [ -f "${xfce_install}/usr/local/lib/libedit.so.0" ]; then
rm ${xfce_install}/usr/local/lib/libedit.so.2 -rf
fi
# 依赖版本 libtinfo.so.5
if [ -f "${xfce_install}/usr/lib/libtinfo.so.6" ]; then
rm ${xfce_install}/usr/lib/libtinfo.so.5 -rf
fi
# 依赖版本 libffi.so.6
if [ -f "${xfce_install}/usr/local/lib/libffi.so.8" ]; then
rm ${xfce_install}/usr/local/lib/libffi.so.6 -rf
fi
fi
# 我们测试驱动, 制作的镜像启动后,我们进入此目录 insmod hello_world.ko 即可
./mk_drv.sh $(pwd)/${diskfs}/lib/modules
# 编译网卡驱动 ( 目前版本内核已集成 e1000 )
# cd ${build_dir}/linux-5.8.6 && make M=drivers/net/ethernet/intel/e1000/ && cd ../..
# 生成 grub.cfg 文件, 增加 console=ttyS0 就会让 qemu 输出日志到 qemu.log, quiet 屏蔽内核过多的信息输出
cat - > ${diskfs}/boot/grub/grub.cfg << EOF
set timeout=3
menuentry "smart-os" {
root=(hd0,msdos1)
linux /boot/bzImage console=tty0 quiet
initrd /boot/initrd
}
EOF
# 生成 /etc/resolv.conf 文件
cat -> ${diskfs}/etc/resolv.conf << EOF
nameserver 8.8.8.8
nameserver 114.114.114.114
EOF
# 生成 /etc/fstab 挂载文件
cat -> ${diskfs}/etc/fstab << EOF
# <file system> <dir> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
EOF
# 生成 /etc/init.d/rcS 文件
title=$(cat<<EOF
\e[0;36m
..######..##.....##....###....########..########..........#######...######.
.##....##.###...###...##.##...##.....##....##............##.....##.##....##
.##.......####.####..##...##..##.....##....##............##.....##.##......
..######..##.###.##.##.....##.########.....##....#######.##.....##..######.
.......##.##.....##.#########.##...##......##............##.....##.......##
.##....##.##.....##.##.....##.##....##.....##............##.....##.##....##
..######..##.....##.##.....##.##.....##....##.............#######...######.
\e[0m
EOF
)
# 生成 /etc/init.d/rcS 脚本
mkdir -pv ${diskfs}/etc/init.d
cat - > ${diskfs}/etc/init.d/rcS << EOF
#!/bin/sh
echo -e "\n“${title}”\n"
# 测试驱动加载
cd /lib/modules && insmod hello_world.ko
# dns 测试 busybox 必须动态编译 动态编译 glibc 已经集成 dns 功能
# qemu 网卡设置(调试方便)
# ifconfig eth0 192.168.100.6 && ifconfig eth0 up
# route add default gw 192.168.100.1
# vmware 网卡设置 ( 调试方便 )
ifconfig eth0 192.168.222.195 && ifconfig eth0 up
route add default gw 192.168.222.2
# exec 执行 /etc/init.d/rc.local 脚本
# 启动 sshd 服务,保证远程连接,调试跟踪非常方便
/usr/sbin/sshd
EOF
chmod +x ${diskfs}/etc/init.d/rcS
# 登陆 login shell ,非 non-login shell
if [ "${with_login}" = true ]; then
# 必须设置这个 /usr/libexe/upowerd 才能启动,否则,就会提示 "name lost, exiting"
echo "export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/usr/local/var/run/dbus/system_bus_socket" >> ${diskfs}/etc/profile
echo "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" >> ${diskfs}/etc/profile
echo "export LD_LIBRARY_PATH=/lib:/lib64:/usr/lib:/usr/lib64:/usr/local/lib:/usr/local/lib64:/usr/lib/x86_64-linux-gnu" >> ${diskfs}/etc/profile
else
# 必须设置这个 /usr/libexe/upowerd 才能启动,否则,就会提示 "name lost, exiting"
echo "export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/usr/local/var/run/dbus/system_bus_socket" >> ${diskfs}/etc/profile
echo "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" >> ${diskfs}/etc/bash.bashrc
echo "export LD_LIBRARY_PATH=/lib:/lib64:/usr/lib:/usr/lib64:/usr/local/lib:/usr/local/lib64:/usr/lib/x86_64-linux-gnu" >> ${diskfs}/etc/bash.bashrc
fi
echo "${GREEN}+++ build diskfs ok +++${NC}"
# 卸载映射
umount ${loop_dev}
losetup -d ${loop_dev}
#----------------------------------------------------------------
#
# 常用命令
#
#----------------------------------------------------------------
# 查看CPU信息cat /proc/cpuinfo
# 查看板卡信息cat /proc/pci
# 查看PCI信息lspci (相比cat /proc/pci更直观)
# 查看内存信息cat /proc/meminfo
# 查看USB设备cat /proc/bus/usb/devices
# 查看键盘和鼠标:cat /proc/bus/input/devices
# 查看系统硬盘信息和使用情况fdisk & disk - l & df
# 查看各设备的中断请求(IRQ):cat /proc/interrupts
# 查看系统体系结构uname -a
# dmidecode查看硬件信息包括bios、cpu、内存等信息
# dmesg | more 查看硬件信息
# modinfo命令可以单看指定的模块/驱动的信息
# 查看设备名称 cat /sys/class/input/mouse2/device/name
# linux为什么访问设备数据先要mount? https://www.zhihu.com/question/524667726
#---------------------------------------------------------------
#
# 查看磁盘内容
#
#---------------------------------------------------------------
./ls_img.sh
#---------------------------------------------------------------
#
# 转换为 vmware 格式, 虚拟机的磁盘类型一定设置为 SATA ,否则启动失败
# SCSI 格式转换, 命令查询 : qemu-img convert -O vmdk -o ?
# 需要二次转换 vmkfstools -i 11.vmdk esxi-compatible.vmdk
#
#---------------------------------------------------------------
qemu-img convert disk.img -f raw -O vmdk disk_sata.vmdk
echo "Run the next script: 03_run_qemu.sh or 04_run_docker.sh"