使用qemu搭建linux内核开发环境详细教程

目录

一.安装交叉编译链(根据自己的需求去安装编译链,我这里安装的是32位)

step1:下载编译链,执行命令:

step2:解压源码

step3:添加环境变量,使你的编译链全局可用   

step4:保存退出后使该变量生效:

二,安装qemu

step1:

step2:

step3:

step4:

step5:

step6:

三.下载内核

step1:下载内核:

step2:配置内核config:

step3:编译内核:

四.使用qemu启动内核

五,制作一个最简单的文件系统

step1:制作一个最小的initrd(initial ramdisk)

step2:编译:

step3:然后将init制作为cpio:

step4:然后我们使用qemu引导一下这个文件系统:

六,使用busybox制作一个真正的根文件系统

step1:下载busybox源码:

step2:解压:

step3:配置busybox:配置为静态编译,避免其他库的问题

step3:编译busybox:

step4:安装:

step5:制作根文件系统:

step6.然后再次引导:

        七.新增使用sd卡的方式去加载镜像


之前一直折腾过几次qemu,但是一直没有做记录,系统重装后每次再装qemu很费尽,踩过的坑又要踩一次,这次花了点时间,将搭建流程做了复现和总结,希望帮到自己的同时也可以帮到别人。

    首先准备一个linux系统,ubuntu或者centos都可以(如何装系统这里不再赘述)。我这里的环境是centos7.4,如果你已经有了ubuntu,那么也不影响,只是再下载源码的方式上有所区别,其余步骤都没有什么区别。

    在这之前先创建一个目录用来存放此次实验的资源,我这里在主目录下创建了一个qemu-lab目录,后续的所有步骤都在qemu-lab目录中

   下面是详细的流程:

一.安装交叉编译链(根据自己的需求去安装编译链,我这里安装的是32位)

step1:下载编译链,执行命令:

wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz

关于编译链的资料参考:

https://www.cnblogs.com/linuxbo/p/4297680.html     arm交叉编译器gnueabi、none-eabi、arm-eabi、gnueabihf的区别

https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/

step2:解压源码

tar -xjf gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf.tar.xz

step3:添加环境变量,使你的编译链全局可用   

gedit /etc/profile

在最后一行加入:

export PATH=$PATH:/qemu-lab/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin

该路径是你编译链加压后的编译工具所在的路径

step4:保存退出后使该变量生效:

source /etc/profile

此时在终端敲arm-linux + tab看下,一切正常的话会看到安装好的编译链

二,安装qemu

step1:

wget https://download.qemu.org/qemu-2.11.0.tar.xz

step2:

tar xvJf qemu-2.11.0.tar.xz

step3:

cd /qemu-lab/qemu-2.11.0

step4:

./configure

step5:

make -j4

step6:

make install

上述步骤完成后,终端敲qemu+tab看下,会有如下显示:

qemu-
qemu-aarch64              qemu-mipsel               qemu-sparc64              qemu-system-or1k
qemu-alpha                qemu-mipsn32              qemu-system-aarch64       qemu-system-ppc
qemu-arm                  qemu-mipsn32el            qemu-system-alpha         qemu-system-ppc64
qemu-armeb                qemu-nbd                  qemu-system-arm           qemu-system-ppcemb
qemu-cris                 qemu-nios2                qemu-system-cris          qemu-system-s390x
qemu-ga                   qemu-or1k                 qemu-system-i386          qemu-system-sh4
qemu-hppa                 qemu-ppc                  qemu-system-lm32          qemu-system-sh4eb
qemu-i386                 qemu-ppc64                qemu-system-m68k          qemu-system-sparc
qemu-img                  qemu-ppc64abi32           qemu-system-microblaze    qemu-system-sparc64
qemu-io                   qemu-ppc64le              qemu-system-microblazeel  qemu-system-tricore
qemu-m68k                 qemu-pr-helper            qemu-system-mips          qemu-system-unicore32
qemu-microblaze           qemu-s390x                qemu-system-mips64        qemu-system-x86_64
qemu-microblazeel         qemu-sh4                  qemu-system-mips64el      qemu-system-xtensa
qemu-mips                 qemu-sh4eb                qemu-system-mipsel        qemu-system-xtensaeb
qemu-mips64               qemu-sparc                qemu-system-moxie         qemu-tilegx
qemu-mips64el             qemu-sparc32plus          qemu-system-nios2         qemu-x86_64

qemu工具都装了,每种工具的含义可以百度下

三.下载内核

step1:下载内核:

https://www.kernel.org/pub/linux/kernel/

step2:配置内核config:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm vexpress_defconfig

这里我们的config选用了网上资料较多,qemu支持也比较好的vexpress_defconfig,你也可以选用其他的

step3:编译内核:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j4

编译成功后会有这行打印,会告诉我们的镜像地址:Kernel: arch/arm/boot/zImage is ready

此时建议建立一个qemu-image目录用于存放所有编译出来的镜像文件,这里我们将内核镜像拷贝到这个目录备用

cp ./arch/arm/boot/zImage ./qemu-image/

四.使用qemu启动内核

因为我目前不太关注uboot,所以此次暂且不去深究uboot启动内核的细节,qemu可以绕过uboot直接指定内核启动,接下来我们直接启动内核

启动内核命令:

qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "init=/linuxrc root=/dev/mmcblk0p1 rw  console=ttyAMA0" -nographic

-M用于指定机器型号

-kernel用于指定内核镜像

init用于指定文件系统起来后执行的linux的第一个进程

root用于指定使用哪个设备作为根文件系统

console用于指定终端

-nographic表示不使用图形界面

NOTE:tty配置错的话将看不到打印输出,这是我踩的最深的坑

tty的区别可以看内核文档:kernel/src/Documentation/serial-console.txt

The format of this option is:

	console=device,options

	device:	tty0 for the foreground virtual console
		ttyX for any other virtual console
		ttySx for a serial port
		lp0 for the first parallel port
		ttyUSB0 for the first USB serial device

	options:depend on the driver. For the serial port this
		defines the baudrate/parity/bits/flow control of
		the port, in the format BBBBPNF, where BBBB is the
		speed, P is parity (n/o/e), N is number of bits,
		and F is flow control ('r' for RTS). Default is
		9600n8. The maximum baudrate is 115200.

上述命令执行完后,会看到终端有这样的打印:

0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 43
input: ImExPS/2 Generic Explorer Mouse as /devices/mb:kmi1/serio1/input/input1
VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
Please append a correct "root=" boot option; here are the available partitions:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.104 #1
[<800147c8>] (unwind_backtrace+0x0/0xe0) from [<80011a18>] (show_stack+0x10/0x14)
[<80011a18>] (show_stack+0x10/0x14) from [<8036ac30>] (panic+0xa0/0x1e4)
[<8036ac30>] (panic+0xa0/0x1e4) from [<80489030>] (mount_block_root+0x1e0/0x274)
[<80489030>] (mount_block_root+0x1e0/0x274) from [<804891d0>] (mount_root+0x10c/0x114)
[<804891d0>] (mount_root+0x10c/0x114) from [<80489328>] (prepare_namespace+0x150/0x188)
[<80489328>] (prepare_namespace+0x150/0x188) from [<80488cb0>] (kernel_init_freeable+0x224/0x234)
[<80488cb0>] (kernel_init_freeable+0x224/0x234) from [<8036617c>] (kernel_init+0xc/0x150)
[<8036617c>] (kernel_init+0xc/0x150) from [<8000e040>] (ret_from_fork+0x14/0x34)

这是因为此时kernel发生了panic,表示没有找到VFS文件系统,但是起码我们的内核是跑起来了,这是让我们有动力进行下一步的基础

五,制作一个最简单的文件系统

我不打算直接去制作一个我们常见的文件系统,如果没有这一步,我们会失去很多乐趣,尽管他很简单,但是我还是先从一个最简单的文件系统走起

step1:制作一个最小的initrd(initial ramdisk)

vim init.c:

#include <stdio.h>
void main()
{
	printf("welcome to qemu test init\n");
	while(1);
}

step2:编译:

arm-linux-gnueabihf-gcc -o init init.c

我们得到了一个init可执行文件

step3:然后将init制作为cpio:

echo init | cpio -o --format=newc > initramfs

此时你会得到一个initramfs,这就是我们将要用到的文件系统,然后将initramfs拷贝到我们的资源文件夹qemu-image中

step4:然后我们使用qemu引导一下这个文件系统:

qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "init=/init  console=ttyAMA0" -nographic -initrd ./qemu-image/initramfs

注意看参数的变化,修改init=/int,表明内核启动后引导的第一个应用程序,-initrd ./initramfs表示指定initrd,initramfs就是刚才我们制作的initrd

运行结果:

Freeing unused kernel memory: 192K (80488000 - 804b8000)
Failed to execute /init
Failed to execute /init.  Attempting defaults...
Kernel panic - not syncing: No init found.  Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.

此时我们观察打印输出,又发生了panic,但是别灰心,一定要分析遇到的问题,我们仔细看下,此时的panic原因,不再是nable to mount root fs on unknown-block 而是No init found,这意味着此时已经加载了文件系统只是没有找到init

这是为什么呢,其实这是因为我们制作的这个init可执行文件有问题,执行失败了,怎么验证呢?,我们可以使用qemu直接执行我们制作的init可执行程序,看有什么问题:

[root@localhost qemu-lab]# qemu-arm init
[root@localhost qemu-lab]#/lib/ld-linux-armhf.so.3: No such file or directory

看完提示,问题清楚了,它没有库,因为我们制作的这个文件系统很简陋,没有库,执行的时候会失败。

所以重新制作:

arm-linux-gnueabihf-gcc -static -o init init.c//使用静态编译,把库给编上

重新打包:
echo init | cpio -o --format=newc > initramfs

然后再引导一次:
qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "init=/init  console=ttyAMA0" -nographic -initrd ./qemu-image/initramfs

结果:

 #0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 43
Freeing unused kernel memory: 192K (80488000 - 804b8000)
welcome to qemu test init

看到这句welcome to qemu test init就代表ok了,表示已经将我们的init可执行文件已经加载起来了

六,使用busybox制作一个真正的根文件系统

有了步骤四的感受,我们再来步骤五,制作一个真正的根文件系统

step1:下载busybox源码:

wget https://busybox.net/downloads/busybox-1.30.1.tar.bz2

step2:解压:

tar xvf busybox-1.30.1.tar.bz2

step3:配置busybox:配置为静态编译,避免其他库的问题

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm menuconfig

NOTE:此时若报错:

  HOSTCC  scripts/kconfig/lxdialog/checklist.o
In file included from scripts/kconfig/lxdialog/checklist.c:24:0:
scripts/kconfig/lxdialog/dialog.h:31:20:error

这是因为缺乏库的问题,导致menuconfig执行失败,我们安装库:
yum install ncurses-libs
yum install ncurses-devel

ubuntu使用apt-get isntall 命令安装

配置界面:
 

Settings  --->  
     [*] Build static binary (no shared libs)

step3:编译busybox:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm -j4

step4:安装:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm install

成功后会有如下打印:

--------------------------------------------------
You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

step5:制作根文件系统:

这个过程有些繁琐,我将这个过程写成一个脚本如下:

gedit creat_cpio_rootfs.sh

#!/bin/sh
busybox_folder="./busybox-1.30.1"
rootfs="rootfs"
echo $base_path
if [ ! -d $rootfs ]; then #判断文件是否存在
	mkdir $rootfs
fi
cp $busybox_folder/_install/*  $rootfs/ -rf
cd $rootfs
if [ ! -d proc ] && [ ! -d sys ] && [ ! -d dev ] && [ ! -d etc/init.d ]; then
	mkdir proc sys dev etc etc/init.d
fi

if [ -f etc/init.d/rcS ]; then
	rm etc/init.d/rcS
fi
echo "#!/bin/sh" > etc/init.d/rcS
echo "mount -t proc none /proc" >> etc/init.d/rcS
echo "mount -t sysfs none /sys" >> etc/init.d/rcS
echo "/sbin/mdev -s" >> etc/init.d/rcS
chmod +x etc/init.d/rcS
if [ -f ../qemu-image/rootfs.img ]; then
	rm ../qemu-image/rootfs.img
fi
find . | cpio -o --format=newc > ../qemu-image/rootfs.img

这里要注意一下,确保你的当前目录是有一个之前我提到qemu-image这个存放镜像的目录,否则脚本会执行出错,或者你也可以修改上述脚本适配你的目录

执行脚本:sh creat_cpio_rootfs.sh

脚本执行后会在当前目录下得到一个rootfs目录和在qemu-image目录下得到一个文件rootfs.img

rootfs.img就是我们要引导的文件系统

step6.然后再次引导:

qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "root=/dev/ram rdinit=sbin/init  console=ttyAMA0" -nographic -initrd ./qemu-image/rootfs.img

此时我们可以看到:

NET: Registered protocol family 17
VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 0
input: AT Raw Set 2 keyboard as /devices/mb:kmi0/serio0/input/input0
rtc-pl031 mb:rtc: setting system clock to 2019-03-11 11:26:19 UTC (1552303579)
ALSA device list:
  #0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 43
Freeing unused kernel memory: 192K (80488000 - 804b8000)

Please press Enter to activate this console. input: ImExPS/2 Generic Explorer Mouse as /devices/mb:kmi1/serio1/input/input1

/ # ls
bin      etc      proc     sbin     usr
dev      linuxrc  root     sys
/ # 

再尝试使用qemu自带的图形界面启动,注意看参数的变化::

qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "root=/dev/ram rdinit=sbin/init  console=ttyAMA0" -initrd ./qemu-image/rootfs.img

 

此时我们的整个内核开发环境就搭建完成了,对于文件系统这块,可以根据自己的需求,尝试制作其他的文件系统类型,我这里只是一个最简单的。

环境搭建起来后,可以很方便的验证自己对内核的一些想法,也可以验证学习些内核的debug工具,非常的方便。

本文的目录结构如下:

[root@localhost qemu-lab]# ls
busybox-1.30.1  creat_cpio_rootfs.sh linux-3.10.104 qemu-image  rootfs

七.新增使用sd卡的方式去加载镜像

前面的文件系统是一个临时的ram文件系统,没有使用到存储分区,所以我们的引导参数中为-initrd。

这次新增一个从sd卡上加载文件系统,文件系统格式为ext3的加载方式,会使用到存储分区mmcblk0。

文件系统的脚本:create_ext3_rootfs.sh,注意最后2句命令的差别

#!/bin/sh
busybox_folder="./busybox-1.30.1"
rootfs="rootfs"
echo $base_path
if [ ! -d $rootfs ]; then #判断文件是否存在
	mkdir $rootfs
fi
cp $busybox_folder/_install/*  $rootfs/ -rf
cd $rootfs
if [ ! -d proc ] && [ ! -d sys ] && [ ! -d dev ] && [ ! -d etc/init.d ]; then
	mkdir proc sys dev etc etc/init.d
fi
 
if [ -f etc/init.d/rcS ]; then
	rm etc/init.d/rcS
fi
echo "#!/bin/sh" > etc/init.d/rcS
echo "mount -t proc none /proc" >> etc/init.d/rcS
echo "mount -t sysfs none /sys" >> etc/init.d/rcS
echo "/sbin/mdev -s" >> etc/init.d/rcS
chmod +x etc/init.d/rcS
if [ -f ../qemu-image/a9rootfs.ext3 ]; then
	rm ../qemu-image/a9rootfs.ext3
fi

#生成一个32M的镜像
dd if=/dev/zero of=../qemu-image/a9rootfs.ext3 bs=1M count=32
mkfs.ext3 ../qemu-image/a9rootfs.ext3  

接着执行下面的操作(root下):

mkdir tmpfs
mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop
cp -r rootfs/*  tmpfs/
sudo umount tmpfs

接着执行命令引导内核:

qemu-system-arm -M vexpress-a9  -kernel ./qemu-image/zImage  -append "root=/dev/mmcblk0  console=ttyAMA0" -nographic -sd ./qemu-image/a9rootfs.ext3

此时跑起来后发现文件系统中无法创造文件,提示是只读文件系统,但是mount命令显示分区都挂载为了可读写,后面发现是append参数中的没有指定rw参数导致的,加上后引导的文件系统就是一个可读写的文件系统,新的命令为:

qemu-system-arm -M vexpress-a9 -m 1024M -cpu cortex-a9 -kernel  ./qemu-image/zImage -append "root=/dev/mmcblk0 rw rootfstype=ext3 mem=512M rdinit=sbin/init  console=ttyAMA0" -nographic -sd ./qemu-image/a9rootfs.ext3

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值