学习 Linux 内核的第一步就是下载 Linux 内核代码, 然后进行编译与运转.
下载内核源代码
内核源码除了 Linus 的官方 github.com/torvalds/li… git, 还有各家发布版的 Linux 内核. 最好去发布版的内核去下载, 这样编译进程中不容易犯错.
我的 Linux 机器是 Ubuntu 22.04 TLS, 所以根据官方文档: wiki.ubuntu.com/Kernel/Sour…, 能够 clone git 下载:
$ git clone --depth 1 -b master git://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/jammy
这儿为了减少下载文件的数量和大小, 挑选只下载最新的代码, 只挑选了master
分支. 下载到 jammy 文件夹.
编译装备
编译之前先要装置编译内核需求的各种装置包:
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex bison libelf-dev -y
装备并编译
$ cd jammy
# 此刻并没有 .config 文件, 履行下面的指令会让你挑选装备, 然后保存.
$ make menuconfig
# 生成 .config 文件
$ ls -lah .config
-rw-rw-r-- 1 sre sre 256K Jun 2 20:58 .config
咱们能够看到 .config 文件里边的各种装备项目, 比方:
CONFIG_VERSION_SIGNATURE="Ubuntu 5.15.0-72.79-generic 5.15.98"
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_TSL2583=m
有些装备项表明一个纯文本的装备, 比方上面的版本号. 别的一些后面是 =y
, =m
或许=n
.
-
=y
:当选项被设置为=y时,表明该选项将作为内核的一部分编译进内核映像(静态编译)。这意味着相关的功用将一向可用,并包括在生成的内核映像中。当体系发动时,这些功用将立即可用,无需加载额定的模块。挑选=y是在构建内核时将特定功用编译到内核中的一种方式。 -
=m
:当选项被设置为=m时,表明该选项将作为可加载模块编译(动态编译)。这意味着相关的功用将编译为独立的模块文件(一般是以.ko为扩展名),并在需求时由内核加载。运用=m选项能够将特定功用作为模块构建,以便在运转时根据需求加载和卸载。
挑选=y
或=m
取决于您对体系需求的权衡。假如您确认某个功用一向需求在内核运转时可用,而且不期望依靠额定的模块加载进程,则挑选=y。假如您期望能够根据需求加载和卸载某个功用,而且不会一向运用该功用,则挑选=m。
请注意,对于某些选项,或许还有其他设置,例如=n
,表明将完全排除该功用的编译。这意味着相关的功用将在内核映像和模块中都不可用。挑选特定的设置取决于您的需求和体系装备。
文字界面装备如下, 最终挑选 Save 到 .config 文件.
编译:
$ make all -j 4 # 运用 4个线程编译, 或许要等很久, 最终生成内核文件 arch/x86/boot/bzImage
...
$ ls -lah arch/x86/boot/bzImage
-rw-r--r-- 1 root root 11M Jun 5 08:59 arch/x86/boot/bzImage
Kernel: arch/x86/boot/bzImage is ready (#1)
$ make help # 检查更多指令
假如遇到下面的犯错:
sre@sre:~/work/exp/jammy$ make all -j 8
...
make[1]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1900: certs] Error 2
能够参看 askubuntu.com/questions/1… 去掉里边证书的部分:
# 能够看到当时的装备, 改成=“”
sre@sre:~/work/exp/jammy$ cat .config | grep CONFIG_SYSTEM_TRUSTED_KEYS
CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
测验发动内核
首先装置 qemu
, 然后发动内核:
$ sudo apt install qemu-system-x86 -y
$ qemu-system-x86_64 -kernel bzImage -append "console=tty0 console=ttyS0" -nographic
SeaBIOS (version 1.15.0-1)
iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+07F8B340+07ECB340 CA00
Booting from ROM...
Kernel Offset: disabled
---[ end Kernel panic - not syncing: No working init found. Try passing init= option to kernel.
能够看到最终内核 panic
, 因为没有任何 root 文件体系, 也没有 init 代码. 这时候, 咱们能够经过 -initrd
来发动, 里边能够包括一个 busybox
.
怎么制造一个 initrd
运用 github.com/aweeraman/k… 提供的东西, 按照说明文档, 履行第一步 ./mk-initrd
就能生成 initramfs.cpio.gz
, 里边包括了 initrc 和 busybox.
再次测验发动:
$ qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz -append "console=tty0 console=ttyS0" -nographic
#这次成功发动
怎么做一个最小的内核并发动进入指令行
制造最小内核
在 Linux 内核源代码根目录履行 make help
, 你能看到各种有关装备的子指令, 比方:
-
defconfig
: New config with default from ARCH supplied defconfig. -
allnoconfig
: New config where all options are answered with no. -
allyesconfig
: New config where all options are accepted with yes. -
tinyconfig
: Configure the tiniest possible kernel.
这儿咱们重视的是 tinyconfig
, 所以咱们先整理一下, 然后运用 tinyconfig
生成 .config
文件, 然后制造 image, 最终运用 qemu
去履行:
$ make mrproper
$ make tinyconfig
$ make all -j 8
Kernel: arch/x86/boot/bzImage is ready
$ qemu-system-x86_64 -kernel bzImage -append "console=tty0 console=ttyS0 init=/bin/sh" -nographic
SeaBIOS (version 1.15.0-1)
iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+07F8B340+07ECB340 CA00
Booting from ROM..
最终发现日志停在 Booting from ROM..
就没有任何消息了.
这是因为 tinyconfig
包括的驱动或许装备太少, 导致没有后续输出, 咱们需求在 tinyconfig
的基础上增加一些装备.
增加装备
增加装备运用 make menuconfig
来修正, 最终保存就好.
-
64-bit kernel
-
Device Drivers -> Character devices -> Enable TTY
-
Device Drivers -> Character devices -> Serial drivers -> 8250/16550 and compatible serial support -> Console on 8250/16550 and compatible serial port
-
General setup > Configure standard kernel features (expert users) -> Enable support for printk
保存上面装备, 而且做一个新的image:
$ make all -j 8
$ qemu-system-x86_64 -kernel bzImage -append "console=tty0 console=ttyS0 init=/bin/sh" -nographic
SeaBIOS (version 1.15.0-1)
iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+07F8B340+07ECB340 CA00
Booting from ROM..
Run /bin/sh as init process
Kernel panic - not syncing: Requested init /bin/sh failed (error -2).
Kernel Offset: disabled
---[ end Kernel panic - not syncing: Requested init /bin/sh failed (error -2). ]---
能够看到 Kernel panic
, 因为咱们仅仅发动 kernel, 没有root 文件体系, 也没有运用 initrd 的ramdisk.
修正config 支撑 initrd
运用 make menuconfig
继续修正
-
General setup -> Initial RAM filesystem and RAM disk (initramfs/initrd) support
-
Executable file formats -> Kernel support for ELF binaries
保存, 然后 make all -j 8
再次制造image, 然后运转:
$ qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz -append "console=tty0 console=ttyS0 init=/bin/sh" -nographic
Run /init as init process
Failed to execute /init (error -8)
Run /bin/sh as init process
/bin/sh: can't access tty; job control turned off
/ # input: ImExPS/2 Generic Explorer Mouse as /devices/platform/i8042/serio1/input/input2
clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x311fac54234, max_idle_ns: 440795352581 ns
clocksource: Switched to clocksource tsc
uname -a
Linux (none) 6.4.0-rc5+ #3 Wed Jun 7 08:19:32 PDT 2023 x86_64 GNU/Linux
/ # ls /bin/
...
/ # ps aux
PID USER TIME COMMAND
能够看到 kernel
发动后履行了 /bin/sh
, 咱们运用 uname
能看到 kernel
的版本号, 可是ps
没有任何输出. 那是因为咱们没有挂载 proc
文件体系. 同时履行挂载 proc
文件体系的脚本在 initramfs.cpio.gz
内部的 init
文件里, 它是一个 shell, 所以要使 kernel
支撑 shell 的 #!
.
再次经过 make menuconfig
修正装备:
- Executable file formats -> Kernel support for scripts starting with #! 2. File systems > Pseudo filesystems -> (/proc file system support & sysfs file system support)
修正完保存, 然后重新制造 image, 而且运转:
$ qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz -append "console=tty0 console=ttyS0 init=/bin/sh" -nographic
ps
PID USER TIME COMMAND
1 0 0:00 {init} /bin/sh /init
2 0 0:00 [kthreadd]
3 0 0:00 [kworker/0:0-eve]
4 0 0:00 [kworker/0:0H]
5 0 0:00 [kworker/u2:0-ev]
6 0 0:00 [mm_percpu_wq]
7 0 0:00 [ksoftirqd/0]
8 0 0:00 [oom_reaper]
9 0 0:00 [writeback]
10 0 0:00 [kswapd0]
11 0 0:00 [kworker/u2:1-ev]
12 0 0:00 [kworker/0:1-eve]
13 0 0:00 [kworker/u2:2-ev]
14 0 0:00 [kworker/0:2]
19 0 0:00 sh
20 0 0:00 ps
怎么制造一个可运转的 ISO 文件
创立文件结构而且复制数据
$ mkdir -p iso/boot/grub
$ cp bzImage iso/boot/
$ cp initramfs.cpio.gz iso/boot/
创立 grub.cfg 文件
$ vim iso/boot/grub/grub.cfg
set default=0
set timeout=10# Load EFI video drivers. This device is EFI so keep the
# video mode while booting the linux kernel.
insmod efi_gop
insmod font
if loadfont /boot/grub/fonts/unicode.pf2
then
insmod gfxterm
set gfxmode=auto
set gfxpayload=keep
terminal_output gfxterm
fimenuentry 'myos' --class os {
insmod gzio
insmod part_msdos
linux /boot/bzImage init=/bin/sh console=ttyS0 console=tty0
initrd /boot/initramfs.cpio.gz
}
装置 xorriso
, mtools
而且制造 ISO image:
$ sudo apt install xorriso mtools -y
$ grub-mkrescue -o myos.iso iso/
$ ls -lah myos.iso
运用 Qemu 测验新的 ISO image
$ qemu-system-x86_64 -boot d -cdrom myos.iso -nographic