本教程是我四天来总结的经验,期间编译了4.19.142和5.13两个版本的内核,打包了20多次initrd,踩了无数坑,希望对大家有帮助!觉得有用的话点个赞吧
咱是15岁学生,制作不易…
预计阅读时间20-30分钟,操作时间1-2小时(不含编译)
1.要做一个能运行的linux kernel,并且不需要外挂.ko内核模块就能加载常用驱动(如SATA&USB)
2.使用busybox制作最小initrd(不含modules也能正常使用的),并且能正常检测硬盘并挂载等等功能
1.跑Ubuntu的电脑一台,或者是虚拟机(*这回是现钱,CPU要好!*)
2.安装可能需要的lib
sudo apt-get install make gcc fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison
(如编译过程中有缺少其他依赖的报错也请一并安装)
3.获取以下源码linux-kernel-5.13 & busybox-1.33.1
linux-kernel可以从腾讯云下载linux-kernel-x.xx.xx.tar.gz压缩包,busybox找官网下压缩包(注意别下成可执行文件,一定要源码!)就行
4.把你下的东西cp到你自己的目录里,比如我是~/LinuxSource
,这个目录其实挂载在一块NTFS格式的虚拟硬盘上(不推荐用NTFS格式,但事实证明没有什么问题)
5.全过程建议使用root用户
因为正常驱动要用.ko模块加载的,咱直接给他跳过这一部分,为的是最小通用镜像嘛,不需要加载模块,也方便(*并不,主要是懒*)
gzip -d linux-kernel-5.13.tar.gz
于是你发现有一个tar文件出现了!解了他!
tar -x -f linux-kernel-5.13.tar
tar解压可能比较慢,稍安勿躁,还可能会有无法提取文件的报错,一般情况下没问题忽略即可
解压之后看一下生成目录里的东西是不是跟压缩包里差不多,如果差不多就差不多了
cd linux-kernel-5.13
make menuconfig
如果你的lib安装全了的话,是可以正常进入menuconfig的,报错了就安lib
menuconfig可能会出现加载不出来卡住的情况,按两下回车就好了
写的很清楚,空格键换模式,上下键换目标,左右键换功能,回车确认,M是编译成模块(本教程不讨论),留空是不要,*是打进内核
右下角有v(+)说明可以向下翻页,有—>说明回车里面有子菜单
第一项留空,第二项我给他起名叫-all-in-one也就是(驱动)全都打包
进内核,
init位置是/sbin/init (busybox init的位置),设备名随便起一个,你也给他叫gensoukyo都没事
这个菜单配置完了,选Exit退出
好的,我们现在在主菜单选Firmware Drivers
再进入有EFI关键字的那个选项(如果你需要UEFI启动内核的话请务必,使用传统bios可忽略)
现在退到主菜单
进入Device Drivers
这一部分操作就多了,准备好把空格键按烂吧,我们要把大量常用驱动打进去
进入Block devices
把除了IDE那个部分的以外的全打上*
带debug的不用打,不能的留M,下同
然后往下找到Default ramdisk size
默认应该是65536KB(64MB),我们给他改成131072KB(128M)大一点还是好的,毕竟现在机子都不缺那点内存
好的,我们现在返回Device drivers
进NVME support,能*全就全打上
返回
进SCSI/啥啥啥的 那个,能*就全打上
返回
进Serial ATA /啥啥啥的那个(即SATA和PATA驱动),能*就全打上
返回
进Network/那个 配置网络驱动,能*全打上
至此,驱动打完了
让我们返回到主菜单
进File System(文件系统选项)
Ext2,Ext3,Ext4全部选上,把你不认识的都留空或者默认不管他
下翻,找FAT文件系统
里面有FAT和NTFS的选项,选上
返回,找到Native language support配置显示语言
找到简中(SC)和繁中(TC)选上,最下面的UTF8也要上
回到主菜单,存一下你的.config文件,至此,内核配置结束
简单来说,你认识的,知道干嘛的,觉得有用的驱动都可以给他干进去,无非就是内核体积大点
不过也不会大太多,按照本文的配置编译bzImage(压缩内核镜像)也就15MB左右
1.linux-5.13/vmlinux
这是一个非常大的,不压缩的内核镜像,不适于启动系统
2.linux-5.13/System.map
System.map用于存放内核符号表信息。符号表是所有符号和其对应地址的一个列表,随着每次内核的编译,就会产生一个新的对应的System.map文件,当内核运行出错时,通过System.map中的符号表解析,就可以查到一个地址值对应的变量名,或反之。
3.linux-5.13/modules.buidin
该文件记录了编译进内核的模块,也就是咱们刚刚打的驱动之类的
4.linux-5.13/arch/x86/boot/bzImage
这个就是咱们要的bzImage,也就是压缩的,可以启动的内核镜像,启动系统就用他配合initrd
5.linux-5.13/arch/x86/boot/compressed/*
这些是其他压缩过的内核镜像之类的,比较混乱,咱们不管他
6.linux-5.13/usr/*
这里面生成了一些initmfs cpio文件,应该是根据内核配置(本文未提及设置initrd文件目录的操作)自动打包的initrd,经过测试好像不太好用,咱们用自己的initrd,后面会讲到
请务必看完再编译!!
回到shell
make bzImage -j[线程数]
#我用的是14线程,i7-10870H / 8G RAM,首次编译只需要二十多分钟
#差一点的处理器可能要数小时,这也是要好处理器的原因了
注意!如果编译过程中不久就出现了以下报错,赶紧Ctrl C暂停
编辑你的.config文件,查找
删掉这一整行
再次编译会让你确认,回车就行,不需要输入y/n
给大家提个醒,你现在直接编译还是会在最后时刻前功尽弃,我编译快可以再来一遍,你们可别踩坑了
再次编译,又有让你确认的,你就一路n就行了
好了,现在你可以放心的编译了,等待过程中不妨研究下busybox的原理
linux2.6之前的initramfs和现在的initrd不一样了,老版本的咱们不讨论,感兴趣的可以查一下,现在的initrd是一个压缩(通常是gz或者是lz4)过的cpio归档文件.
其中包括了一个给内核提供初始化环境的最小根目录,通常情况下初始化完成后要切换到真正的根目录(通过挂载),不过有些嵌入式系统直接使用ramdisk作为根目录了.
initrd由bootloader在加载内核的同时加载进内存,内核解压initrd到虚拟的ramdisk上,作为根目录.
内核启动完毕后会调用初始化进程(通常是/sbin/init
),完成用户态之前的初始化(比如创建设备节点,初始化控制台,挂载真正根目录等等)
这里我们用到了busybox制作
busybox是啥?让咱看下百度百科
BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Android 系统(//此处似乎有误,应为linux)的自带的shell。
通俗的讲,busybox包括了大量可执行程序的功能,甚至包括init,且可以静态编译(即不需要lib亦可运行)
所有生成的命令其实都是指向bin/busybox不同功能的符号链接,说白了二进制文件其实就一个
这里我们主要用它提供的init,sh,mount等功能
1.解压你的busybox源码
bzip2 -d busybox-1.33.1.tar.bz2
tar -x -f busybox-1.33.1.tar
2.配置busybox
cd busybox-1.33.1
make menuconfig
#老朋友 menuconfig 又见面了
相比之下,busybox就简陋多了
打开settings
找到
也就是让他静态编译,老规矩,打*
busybox配置就结束了,剩下的咱不用管了,退出,记得保存你的配置
开始编译
make install -j14
#小工程犯不着用多线程 但是我有14线程为什么不用呢?
#不会真的有人嫌程序运行太快吧
编译结束,生成了一个_install
目录,我们把东西cp过去
mkdir ./../rootfs
cp _install/* ./../rootfs
cd ./../rootfs
ls
#输出
#bin sbin usr linuxrc
ls sbin
#输出
... ... ...
... init ...
可以看到,已经有一个根目录的雏形了,并且sbin/init
初始化进程也有了
这时候我们要把它完善
或许你注意到,rootfs/linuxrc
这个文件,他其实也是指向init的,只不过名字不一样,我们给他改了,防止出问题
mv linuxrc init
现在完善根文件系统
mkdir dev etc mnt proc sys
#生成必要目录
cd dev
mknod console c 5 1
#创建控制台字符设备,没有会导致报错无法运行
mknod null c 1 3
ln null tty0
ln null tty1
ln null tty2
cd ..
#创建tty字符设备,尤其是tty2,没有它控制台会疯狂报错
#注意 特殊设备文件是不能复制的,只能移动,所以如果需要更改工作目录请使用Ctrl+x
目录结构咱已经搞出来了,实际上现在打包就能开机了,只不过是瘸子腿儿走路,啥也干不了,因为初始化没有彻底完成
我们要偷懒用一下Ubuntu的初始化脚本
请把Ubuntu系统的/boot/initrd.img-x.x.x-xx-generic
这个initrd文件给他拉过来
cp /boot/initrd.img-5.8.0-55-generic .
ls
#输出
#rootfs busybox-1.33.1 initrd.img-5.8.0.55-generic
这时候你可以用归档管理器打开,会发现,哎,跟咱们做到initrd结构完全不一样啊?
这就对咯!这个文件其实是个缝合怪,好几个部分拼在一起了
让我康康!
apt install binwalk
#binwalk是一个查看文件结构的利器,会告诉你某个文件什么地方由什么组成
binwalk initrd.img-5.8.0-55-generic
#输出如下图
奥,原来是这样,它前边是cpio,后面才是打包的initrd
找到输出的第一个正经压缩格式的位置
看来用得是lz4压缩的initrd
记下来位置
dd if=initrd.img-5.8.0-55-generic bs=4641792 skip=1>>out.lz4
#把它从lz4的位置噶(割)下来
mkdir ubuntu_initrd
lz4 -d out.lz4
#解压第一层lz4
mv out ubuntu_initrd
cd ubuntu_initrd
cpio -idmv#解压第二层cpio
rm -f out
#删了它以绝后患
ls
#输出如下
我们呢,只需要用到一个文件init
和一组文件夹scripts
编辑init可以发现是一个sh文件,里面大概是写的怎么挂载/proc之类的,反正是完成初始化,调用scripts
里的sh函数
现在把init和scripts复制过去,剩下的就没用了
cp init ./../rootfs/init.2
#因为已经有一个同名文件了,所以我们给他重命名成init.2,即初始化的第二步
cp -r scripts ./../rootfs
cd ./../rootfs
ls
#输出
#bin dev etc init init.2 lib mnt proc sbin scripts sys usr
然后我们要准备etc目录下的东西,也就是初始化的配置
这些是必要的
有关这部分的配置我参考了一下这里,讲的听清楚的
我觉得可以再讲一遍
来到咱们的busybox1.33.1/examples/bootfloopy/etc
目录
把里面所有东西复制到rootfs/etc
里,编辑etc/inid.d/rcS
rcS这个文件是init之后调用的脚本,你也可以加点自己的东西进去
在后面加一句/init.2
调用咱们之前复制过去的ubuntu初始化脚本
然后再编辑etc/inittab
懒得写了,引用一下
inittab原始内容
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
简单说就是每一行表示一种执行策略,例如第一行表示在系统启动时执行RCS,第二行表示如果sh挂了会自动重启sh,第三行表示使用tty2启动的sh启动时会首先询问,第四行表示使用ctrl+alt+del快捷键时执行的程序。这里我们改下配置内容
修改后的
::sysinit:/etc/init.d/rcS
console::respawn:-/bin/sh
将sh启动的控制程序交给console,并且删除其他操作。
至此,initrd内容准备完毕
cd ..
find ./rootfs | cpio -H newc -o > initrd.cpio
#打包cpio
gzip initrd.cpio
#使用gzip压缩
mv initrd.cpio initrd.img
做了这么多工作,现在终于可以开始测试了
你需要准备:
1.bzImage
2.initrd.img
3.Ubuntu安装盘
4.DiskGenius软件
用DiskGenius创建一个虚拟磁盘文件,包括一个EFI区和一个EXT4区,然后映射EFI到本地卷,注意要用读写模式
需要把grub从Ubuntu的安装盘里把/boot
和/EFI
这俩目录复制出来
把bzImage和initrd.img放到虚拟磁盘/boot
目录里
修改/boot/grub/grub.cfg