编译Linux内核并运行一个最小镜像

需求

公司的专用服务器使用自定义Linux内核,运行一个由busybox构成的最小镜像. 由于某些原因, 使用的内核来自其他人已经编译好的,并且也拿不到配置文件。 现在需要:
1. 添加某个专用驱动到内核中,并且添加一些脚本到新的系统运行镜像包initrd中
2. 兼容以前的系统镜像,不能出现使用旧的内核搭配新镜像时无法启动的问题

本文把用到的知识点记录下来,便于有相同需求的同学参考。
系统内核称为bzImage或kernel , 系统镜像称为 initrd

包含知识点

  1. Linux启动过程之initrd
  2. BootLoader 之 extLinux
  3. 编译Linux内核的方法
  4. 内核编译时如何选择系统自带的驱动
  5. 如何将私有驱动编译到内核中
  6. 启动内核时,出现 kernel Panic - not syncing 的解决办法
  7. 开机后键盘无法使用的问题解决
  8. 使用busybox制作initrd

本文中只叙述某些关键点,具体细节可以参考每一节中给出的链接

1. Linux启动过程之initrd

  1. 机器启动时,首先是 BIOS 运行 ,在磁盘的MBR中,找到BootLoader。
  2. BootLoader运行, BootLoader配置文件会指出系统启动的某些参数, 比如在哪里找到 kernel , 在哪里找到 initrd。
  3. 然后开始加载 kernel , 之后加载initrd 进入到 early user space状态。 initrd 有两种技术,旧的一种使用镜像文件,叫做 init ram disk , 新的一种使用ramfs归档文件,叫做 init ram filesystem ,可能是由于历史原因,ubuntu的系统镜像虽然是新的技术,但文件名仍然看起来像是旧的 intrd*.
  4. initrd启动后,如果是旧的技术ram disk,运行的第一个程序是镜像中的 /linuxrc 脚本 , 如果是新的ram filesytem 运行的第一个程序是 /init脚本 , 一般会在脚本的最后使用 switch_root命令切换根目录到实际的磁盘根目录中。
    由于我司服务器的特殊性, 只是把磁盘挂载为 /data 目录用来存储数据, 系统仍运行在 initrd 环境下,所以没有最后这一步。

关于两种技术的区别, 请参考内核源码目录下 Documentation/initrd.txt 和 IBM: Linux2.6 内核的 Initrd 机制解析

要制作支持initrd的内核 参考这个链接 CSDN:移植linux 内核支持ramdisk

2. BootLoader 之 extLinux

extLinux是一种启动引导,优点是支持多种启动介质,配置简单。

一般用法是

mkdir -p /boot/extlinux
extlinux --install /boot/extlinux
cat mbr.bin > /dev/XXX  ##mbr.bin 在extlinux的安装目录下,如果是系统自带的的则可能在  /usr/lib/EXTLINUX/  下, 如果没有,请自行搜索文件位置

配置文件在安装目录的 extlinux/extlinux.conf

配置文件中的参数会传给内核 ,通过指定特定的的参数, 可以用来调试内核问题。 系统启动后可以在 /proc/cmdline 中查看

/root # cat /proc/cmdline 
vga=0x305 loglevel=3 initrd=/rootfs BOOT_IMAGE=/bzImage 

extlinux 官网用法介绍

extlinux.confg 配置文件详解

3. 编译Linux内核的方法

Configuring a Linux Kernel

上面的链接说明了如何编译一个内核

一般系统的步骤为:

make menuconfig   #该步骤可能会提示缺少 ncurses , apt install libncurses5-dev 即可
#配置好选项, 关于如何配置,参考下一节
make -j4         #开始编译,指定4个线程编译,根据配置的不同,这里可能花费几个小时
make modules_install   #安装模块
make install           #安装kernel,initrd 并自动更新引导  内核名称为vmlinuz*  

由于公司的服务器使用busybox最小镜像,只需要编译内核,不需要生成initrd(后面第8节说明如何使用busybox生成initrd), 编译步骤为:

make menuconfig 
make -j4 
make bzImage 

将 bzImage 拷贝到服务器,然后在 extlinux.conf 中指定为 kernel选项的值即可。 或者 extlinux.conf 中kernel的值为一个符号链接, 这样只需要修改符号链接即可

4. 内核编译时如何选择系统自带的驱动

公司服务器硬件比较特殊, 如果直接使用默认的配置生成bzImage,开机后提示没有硬盘,同时键盘也无法使用,无法进入系统调试。

通用的内核选项可以参考这个链接
简书:Linux内核编译与管理

如果编译的内核启动后提示找不到硬盘或者其他驱动问题,可参考这个链接:
CSDN:制作最小linux内核

如果要去除所有不必要的驱动和功能, 只保留必要的功能,也可参考上面的链接
CSDN:制作最小linux内核 , 该文章主要用到命令 lspci -v , lsmod , modinfo

键盘驱动问题在第7节中专门解决。

5.如何将私有驱动编译到内核中

在 linux 内核中增加程序需要完成以下三项工作:
1. 将编写的源代码复制到 Linux 内核源代码的相应目录
2. 在目录的 Kconfig 文件中增加新源代码对应项目的编译配置选项
3. 在目录的 Makefile 文件中增加对新源代码的编译条目

参考这个链接
CSDN:Linux内核开发之将驱动程序添加到内核

6. 启动内核时,出现 kernel Panic的解决办法

旧式 init ram disk initrd的块大小必须和内核编译时的值匹配才能启动
如果你仔细阅读了第1节,应该不会出现这个 kernel panic。

然而, 由于公司的服务器内核配置的块大小未知,并且新编译的内核要和以前的兼容,所以必须知道原来的块大小是多少. 大致流程为:
1. 生成一个512k块大小的系统 , 并把原来的镜像文件拷贝到该镜像中
2. 使用该镜像作为initrd启动
3. 检查内核的出错信息并根据该信息修改块大小

bash# dd if=/dev/zero of=../initrd.img bs=512k count=100
bash# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img /mnt
bash# cp -r * /mnt        #拷贝需要的文件到镜像
bash# umount /mnt
bash# gzip -9 ../initrd.img

修改initrd为上面生成的压缩文件, 重新启动系统,出现 kernel panic

Kernel panic - not syncing :VFS: Unable to mount root fs on unknown-block(2,0)
Kernel Offset:0x0 from 0xffffffff81000000 (relocation randge :0xffffffff80000000-0x0xffffffff9fffffff)

0xffffffff81000000 和 0xffffffff80000000 相差16M, ‭1677721字节.
在extlinux.conf 文件中添加配置

APPEND ramdisk_size=‭16777216‬

重新启动成功。 因此块大小应该为 16M .

删掉 APPEND ramdisk_size=‭16777216 根据第1节的方法, 修改内核配置参数

#make menuconfig
打开配置菜单,修改两个配置项,分别是:
- General setup–>选择 Initial RAM filesystem and RAM disk…… 项
- Device Drivers–>Block devices–>选择 RAM block device support 项
- device driver->block device里的一个选项,Default Ramdisk 设置ramdisk的大小‭16777216‬

保存退出, 重新编译即可

7.开机后键盘无法使用的问题解决

可能是主板的原因, 编译的内核启动后无法识别键盘, 再bootLoader能使用的键盘,在开机后反而无法使用.
键盘驱动问题,请参考这个链接
linux-usb
该链接说明了 鼠标/键盘/手柄的驱动配置方法

8. 使用busybox制作initrd

步骤
1. 下载 busybox 并解压
2. 进入目录 make menuconfig ,并配置 Busybox Settings —> Build Options —> Build BusyBox as a static binary (no shared libs) —> yes
3. make
4. make install
5. 需要的系统文件在 busyboxdir/_install/ 下 ,拷贝所有文件到镜像的挂载目录 , 如果使用 init ram disk , 修改 /linuxrc 脚本 为如下内容

#! /bin/sh

mount -n -t proc proc /proc
#
# disable console login
grep -q console=null /proc/cmdline && sed -i s/^tty/#tty/ /etc/inittab
#
# this is a traditional mechanism for initrd
# notify kernel remount /dev/ram0 as root
echo 0x0100 > /proc/sys/kernel/real-root-dev
umount -n /proc

卸载镜像, 压缩镜像, 请参考第一节

如果使用 ram filesystem , 删除 /linuxrc 添加 /init

#!/bin/sh
#init
mount -t proc none /proc
mount -t sysfs none /sys

exec /bin/sh

打包所有文件为 cpio文件并压缩

find . | cpio --quiet -H newc -o | gzip -9 -n > /boot/initrd.img

参考链接
https://gitlab.eurecom.fr/snippets/23

你可能感兴趣的:(linux,kernel,initrd)