Linux内核编译与启动最小镜像制作

Linux内核才是Linux操作系统最为核心的内容。内核实现了对硬件的管理,为应用软件提供了使用硬件的接口。

内核编译

整个内核编译的过程非常简单,但是内核编译需要花费比较长的时间。这主要是因为内核的代码非常多。当然,如果你​的​计算机性能强劲,时间会短很多。另外需要注意的是,建议在虚拟机环境下编译,这样避免错误导致系统问题。

环境准备

  1.  Ubuntu 18.04版本(历史版本:Index of /releases
  2.  linux-5.9.6内核源码(历史版本:Index of /pub/linux/kernel/v5.x/
  3.  40G盘硬(编译时会产生大量文件)
  4.  4G内存 (64位最小内存大小)

开始编译

对于Linux开发人员而言,内核的学习和开发才是终极目标。因此我们今天先介绍一下内核的编译。需要知道的是,Linux内核的编译和安装并非想象的那么难,那么复杂,其实只需要几条命令就可以搞定。

  • 查看系统版本
uname -mrs

Linux 4.15.0-200-generic x86_64
  •  解压源码linux-5.9.6内核源码
tar xvf linux-5.9.6.tar.xz
  •  安装编译工具以及其它一下依赖的软件包。
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison
  • 配置内核代码
## 1.进入源代码目录

cd linux-5.9.6


## 可以通过如下命令启动配置界面进修生成.config

make menuconfig


## 或者从当前机器的启动目录拷贝配置信息到源代码目录。这步操作的意思是我们编译内核的配置采用用当前环境一致的配置。

cp -v /boot/config-$(uname -r) .config
  • 编译内核
## 通过如下命令就可以编译内核了
make 

## 安装模块

sudo make modules_install

## 安装内核

sudo make install
  • 重启,验证版本
uname -mrs

Linux 5.9.6 x86_64

可能遇到的问题‍

  1. 没有规则可制作目标debian/certs/debian-uefi-certs.pem,由certs/x509_certificate_list需求停止
    ## 在要编译的内核目录下编辑一下配置文件即可。简单的方式是执行如下命令
    vim .config
    
    ## 然后找到CONFIG_SYSTEM_TRUSTED_KEYS,将其设置为空,也就是下面这个样子。
    CONFIG_SYSTEM_TRUSTED_KEYS=””

启动盘制作

制作Linux,首先该系统是放到磁盘上的,所以涉及启动引导操作系统的知识,我们使用著名的Grub引导程序来实现,Grub执行之后,由它来加载OS kernel和文件系统《计算机是如何启动的》。我们使用Qemu来试验,原理与直实硬件完全一样,唯一不同的是Qemu的磁盘可以用Host OS的文件来代替。其关键过程列在这里:

  1. BIOS阶段:计算上电后,系统从主板上的BIOS程序运行,检测系统,初始化运行环境
  2. 加载bootloader阶段:BIOS依次扫描硬盘,如果某个硬件的第一个扇区(512字节)的最后两字节为0x55和0xAA,则该硬盘为启动硬盘,该扇区为主引导记录(Master boot record,缩写为MBR),BIOS将该扇区加载到0x7C00内存处,然后跳到该地址开始执行bootloader
  3. Bootloader加载OS阶段:Bootloader开始执行,由于它只有512字节在内存,所以这512个字节的功能是将它剩下的代码从它后面的扇区(第2扇区,第3扇区,直到……第N扇区)加到到内存,Bootloader 代码完整加载到内存;然后bootloader读配置文件,然后从磁盘中加载kernel文件和根文件系统initrd到内存,最后跳到kernel开始执行OS
  4. OS kernel启动:OS kernel开始做系统初始化,将根文件系统initrd解压缩,加载到根文件,运行init进程

简化版本启动过程,BIOS是主机(Qemu)提供的,而Bootloader,OS kernel和initrd都需要制作安装。

组件软件版本
BootloaderGrub2.00
OS kernellinux kernel4.15
initrdmkinitramfsxx

环境准备

软件版本构建方式
Ubuntu18.04- 桌面版直接安装
kernel4.15源码编译
grub-install2.00~rc1源码编译安装
qemu2.10.0源码编译安装
fdisk2.20.1直接安装
losetupxxx直接安装
mkinitramfsxxx直接安装

开始制作Linux

  • 创建128M磁盘文件
## 使用dd命令,创建一个128M大小的文件,命令如下:

dd if=/dev/zero of=disk.img bs=1M count=128
  • 对磁盘分区,整个磁盘只建一个分区

## 使用fdisk命令对disk.img磁盘进行分区,使用n命令创建新的、主分区,该分区为整个磁盘大小,命令如下:
fdisk disk.img

## 分区后可以使用, 查看分区情况
fdisk -l disk.img

  • 将磁盘分区关联到/dev/loop7设备 
## 查询磁盘情况

fdisk -l disk.img


Disk disk.img: 67 MB, 67108864 bytes
41 heads, 32 sectors/track, 99 cylinders, total 131072 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xa711cdff

 Device Boot      Start         End      Blocks   Id  System
disk.img1            2048      131071       64512   83  Linux


## 磁盘最开始扇区是MBR,里面有分区表,记录磁盘上有多少分区,每个分区从哪个扇区开始,以及分区占用多少扇区,往往第一个分区的开始扇区并不是从第2个扇区开始。

## 可以看到该磁盘只有一个主分区,是从第2048个扇区开始,使用losetup命令将该分区与一个/dev/loop7设备进行关联

## -o表示该分区在disk.img的偏移量(字节为单,即2048 x 512 = 1048576 )

losetup -o 1048576 /dev/loop7 disk.img
  • 格式化分区和挂载分区

## 接下来将分区格式化为ext3,命令如下:

mkfs.ext3 /dev/loop7

## 格式化后,将该分区挂载到mnt目录
mkdir mnt
mount -t ext3 /dev/loop7 ./mnt/
  • 安装grub

## 磁盘和分区已经做完了,下一步是安将Grub
## 运行grub-install命令之后,会将disk.img磁盘第一个扇区修改成MBR
## 同时将grub代码安装到第一个分区之前的扇区里(通常是0号到64号扇区之间),
## 最后将grub代码运行所需要其它模块和配置文件保存到 ./mnt/boot/grub目录下。

grub-install –boot-directory=./mnt/boot/ –target=i386-pc –modules=part_msdos disk.img
  • 下载、编译内核

## 使用 x86_64 创建.config配置文件,
make x86_64_defconfig

## 仅编译并打包
make bzImage -j2

## 将编译好的文件复制到boot目录中
sudo cp arch/x86/boot/bzImage ./mnt/boot/
  • 制作initrd

## 使用mkinitramfs命令可以制作简化版本的文件系统, 使用mkinitramfs命令生成极简的initrd,放到boot目录下

mkinitramfs -o ./mnt/boot/initrd
  • 编写grub.cfg,让Grub引导kernel运行

## 写grub.cfg配置文件,告诉Grub从哪个目录可以找到bzImage和initrd文件

vi ./mnt/boot/grub/grub.cfg

menuentry “FreshLinux” {
linux (hd0,msdos1)/boot/bzImage  root=/dev/sda1 console=tty0
initrd (hd0,msdos1)/boot/initrd
}
  1. hd0表示第一个硬盘,而msdos1表示该硬盘的第一个分区(注:Grub对硬盘和分区的标识略有不同,硬盘从编号0开始,而分区却从编号1开始)。
  2. linux (hd0,msdos1)/boot/bzImage console=tty0 表示:系统第一个硬盘,第一个分区的boot/bzImage文件是内核压缩镜像,而后面的console=tty0是内核启动参数,告诉内核输出到控制台上,而非图形化界面, root=/dev/sda1 让其拥有root权限。
  3. initrd (hd0,msdos1)/boot/initrd 表示:系统第一个硬盘,第一个分区的boot/initrd是根文件系统。
  • 保存做好的磁盘

## 只需将解挂载/dev/loop7即可将 刚刚建立的文件内容刷新到disk.img磁盘文件上

umount /dev/loop7
losetup -d /dev/loop7
  • 从磁盘运行Linux

## 使用qemu将它运行起来
qemu-system-x86_64 -hda disk.img -m 1024

然后进入Grub界面,选择我们刚刚制作的FreshLinux

接下来进入kernel启动,接着运行init进程,最后运行sh进程,等待用户输入命令,输入ls和pwd命令的运行过程:

在VM中运行

上面已经制作好最小的linux镜象了,如何将他做为硬盘在VM中启动

  • 将其转为化vm所支持的硬盘格式
# qemu-img 转换命令可以执行多种格式,包括之间的转换qcow2,qed, raw,vdi,vhd,和vmdk

# 查看文档格式
qemu-img info disk.img
image: disk.img
file format: raw
virtual size: 128M (134217728 bytes)
disk size: 128M

# 转换为vmdk格式

qemu-img convert -f raw -O vmdk  disk.img image.vmdk
  • 创建一个空白的虚拟机

 

主要参考

Linux内核编译很简单,6步编译一个自己的内核

从零开始制作Linux

计算机是如何启动的

VMware虚拟机如何设置从U盘启动

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
编译过程: 1). 解压后默认的文件夹位置是在D:\Linux-0.11,如果你不是将文件解压到该目录下, 你要修改MinGW32目录下的MinGW32.bat文件,将里面的PATH指向MinGW32的bin目录. 2). 打开Linux-0.11目录,双击MinGW32.bat快捷方式,打开控制台. 3). make 一下,生成1.44M的Boot.img软盘镜像,要清除编译结果请"make clean" 4). 如果安装了bochs,直接双击bochsrc.bxrc即可运行Linux-0.11了. 5). 也可用其它虚拟机加载Boot.img后运行,如果出现Kernel panic,请把虚拟机里的硬盘删了 6). 在出现Insert root floppy and press ENTER以后,将rootimage-0.11.img载入虚拟软驱,回车 这就是能在windows环境下编译Linux 0.11了,不是在Cygwin,也不是在虚拟机里,而是使用MinGW. 下面是在Windows下编译Linux 0.11会遇到的问题和对原文件作的修改: 1.赵炯博士已经将汇编程序中引用的C变量(包括嵌入汇编的C变量)的下划线去掉了,但MinGW的gcc可能是为了与其它Windows下的编译器保持兼容,并不能识别这些不带下划线的C变量,因而还得把原先已经在汇编程序中去掉下划线的C变量加上下划线,同时也要把被C程序引用的汇编程序中的变量加上下划线. 2.MinGW中不带as86编译器,因而把boot目录下原先用as86编译的bootsect.s和setup.s两个程序修改成能用nasm编译的程序.并且更名为bootsect.asm和setup.asm. 3.在Makefile作的主要修改: 在LDFLAGS中加了--image-base 0x0000 将elf_i386改成i386pe 将cd 与 make 之间的;改成&,如cd kernel ; make 改成cd kernel & make MinGW中没有sync这个程序,可以把它注释掉,更简单的办法是写一个sync.c,这个sync.c只包含一个空的main函数,编译成sync.exe 因为类似的原因,make dep会出错 4.生成的system文件是PE格式的(PE是Portable Executable的简称),这是windows下的可执行文件的格式,显然是不能直接执行的,必须加以转化.我实现了通过两种方式加以转化. 1)写一个程序Trans.cpp将system.exe里的代码和数据从PE文件里解析出来,生成一个system.bin文件,这个文件是能被setup模块直接加载的.我已经将这个程序放在了Linux-0.11的tools目录下,要微软的编译编译. 2)自己写一个PE Loader,这种方式比较麻烦,但是想想自己也能做一个PE Loader,还是满有成就感的,尽管这是一个最简单的Loader.代码是加在Linux-0.11-With-PE-Loader\boot目录下的setup.asm文件里,里面有详细的注释. 5.对tools下的build.c作了修改,使其能生成可引导的1.44M的软盘镜像文件Boot.img 6.在Link的过程中,init目录下的main.c会出现以下错误: boot/head.o(.text+0x540c):fake: undefined reference to `_main' init/main.o(.text+0x16f):main.c: undefined reference to `_alloca' init/main.o(.text+0x174):main.c: undefined reference to `__main' make: *** [tools/system.exe] Error 1 第一个和最后一个错误还好理解,但中间那个错误那就莫明其妙了,因为Linux 0.11根本没有这个函数,在gcc的编译选项里也有-nostdinc .有一个解释是main函数不是一个普通的函数,MinGW gcc会对它作特殊的处理.解决的办法其实也很简单,把main.c下面的main函数改名为_main,或者是干脆把它改成另外一个函数,就改成start吧.记得把head.s里的_main也改了. 在最后,要感谢《自己动手写操作系统》的作者于渊,其实我也是先将原先只能在Linux编译的书里源代码用MinGW移植到Windows下编译的过程中才试着在Windows下编译Linux 0.11源代码的,有了在Windows下编译Linux 0.11源代码的经验,移植高版本的源代码,像0.12,0.95,0.96等等版本应该不会有太大的麻烦了。 也要感谢Linux内核完全注释的作者赵炯博士,是他拉接了操作系统与操作系统爱好者的距离. 最后,我也非常想和操作系统爱好者们共同交流心得体会,也希望能多认识一些朋友. 我的网名:flyfish 我的QQ:785606288 E-mail:[email protected] 另外,要转载请保持本文件的完整性,请尊重别人的劳动果实. 修改日志: 08/3/29 修改了一下Makefile,旧的Makefile在某些文件更新后还会重新编译。 修改了Trans.cpp中的一个dug,该dug在translate MinGW gcc编译的程序时可能会出错。用MinGW gcc 编译的程序的VirtualAddress的形式可能是0xFFC1000这样的形式,其实0x1000才是它的VirtualAddress 08/4/2 修改了下MinGW32.bat,现在已经不用重设路径了。 08/4/4 Trans.cpp还是有错,如果VirtualAddress>0xffff,那么生成的system.bin就错了,bochs调试时会一直重启。 权宜之计,把0xffff再改成0x3ffff,这样VirtualAddress就不能大于0x3ffff,不知谁有更好的解决方法,

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值