译者序
作为第一篇翻译文档,一方面对于翻译经验不足、措辞不当,另一方面对于linux我也是初学者,很多东西尚处于待学阶段,而翻译此文的动机就是我要学习如何跨平台进行文件系统的编译,所以翻译的结果肯定有许多不到之处,欢迎大家指正,也渴望和大家一起学习进步!
祝枫
2016年6月17日于深圳
https://balau82.wordpress.com/2012/03/31/compile-linux-kernel-3-2-for-arm-and-emulate-with-qemu/
每年市场都会生产大量的运行于ARM核的新产品,并且能够运行Linux等操作系统。然而这些产品中的大多数都相当昂贵(想一想智能手机、开发工具或者评估板),由于像QEMU等软件仿真器,使方便地在ARM上探索Linux的世界成为可能。
我将展示怎么样编译内核并仿真启动。为了简化,启动过程将不包含整个完整的文件系统而是使用一个最小的内存磁盘显示内核执行一个程序的过程。
我选择模拟VersatileExpress产品,因为它支持主流的Linux内核和主流的QEMU。并且,这个硬件平台使用Cortex-A9核,一种今天在很多智能手机上使用的ARMCPU。
为了跟着我的步骤,你需要一些工具。
首先,我做的所有事都是在Linux机器上进行的,最好是一个Debian测试发行版,使用bashshell。为了管理内核的编译,GNU make应该是已经安装的(它通常在build-essential安装包内)。
为了编译ARM架构的内核,必须安装一个交叉编译器。传统编译器和交叉编译器之间的区别是传统编译器运行在一种架构(例如x86_64),产生运行在相同架构上的二进制文件。而交叉编译器产生运行在不同架构(此例中ARMv7)上二进制文件。根据你的版本和安装选项,你可以选择不同的工具链:
Ÿ Emdebian: 这儿是安装指南;
Ÿ Linaro:如果安装了最新版的ubuntu,可以使用”sudoapt-get install gcc-arm-linux-gnueabi”命令安装;
Ÿ Fedora ARMcross-toolchain;
Ÿ Sourcery Codebench (was CodeSourcery); 只有在注册的情况下才免费。
Ÿ other toolchain suggestions byeLinux.
交叉编译器提供了一个程序集,它们以一个标识被编译文件的库文件和二进制接口的架构和操作系统的前缀开始,主要是GCC和binutils。在我的实例中我采用了拥有”arm-linux-gnueabi-”前缀的Emdebian工具链。
最后我使用QEMU仿真器,对应的仿真ARM硬件的程序是”qemu-system-arm”。你必须根据你的版本安装正确的软件包;有时候一些版本把QEMU软件包称为不同的名字,例如Ubuntu把它打包成”qemu-extras”软件包。
建立一根空的目录,然后创建一个包含如下简单C代码的名为”init.c”的文件:
1 2 3 4 5 6 |
#include
void main() { printf("Hello World!\n"); while(1); } |
然后在相同的目录按顺序执行如下命令:
1 2 3 4 5 6 7 8 9 10 11 |
wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.tar.bz2 tar xjf linux-3.2.tar.bz2 export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabi- cd linux-3.2 make vexpress_defconfig make all cd .. arm-linux-gnueabi-gcc -static init.c -o init echo init|cpio -o --format=newc > initramfs qemu-system-arm -M vexpress-a9 -kernel linux-3.2/arch/arm/boot/zImage -initrd initramfs -serial stdio -append "console=tty1" |
内核编译(”makeall”命令)根据你的电脑性能可能花费几分钟或者几个小时。最后一个命令打开一个QEMU窗口,它将会显示一个黑色背景和大量的启动信息,且在最后会显示一个”HelloWorld!”字符串。
步骤如下:
1.获得Linux内核文件源码
2.编译准备
3.配置并编译
4.准备并制作内存磁盘
5.模拟内核启动和内存磁盘的执行
主流Linux内核的官方网站为www.kernel.org,我使用的内核版本是3.2,注意如果你想使用一个不同的版本,你可能会得到不同的结果,尽管这儿使用到的绝大多数功能是非常简单的,不应该因为版本不同而改变。
从FTP site下载linu-3.2.tar.bz2,或者直接运行命令行:
wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.tar.bz2
这时解压内核源码,直接在相同的目录下运行如下命令:
tarxjf linux-3.2.tar.bz2
此时将会自动创建一个名为linux-3.2的包含linux内核所有源码的子目录。
我们将采用交叉编译工具来为ARM架构进行编译,所以我们需要告诉linux编译系统如何去做。这儿有两个环境变量:ARCH和CROSS_COMPILE。ARCH的有效值基本上是”arch”目录的子目录;对于CROSS_COMPILE,我们需要提供工具链的前缀,就是编译器程序的名字除去最后gcc。例如我们使用arm-linux-gnueabi-gcc,我们需要设置CROSS_COMPILE为arm-linux-gnueabi-。在终端中,执行:
exportARCH=arm
exportCROSS_COMPILE= arm-linux-gnueabi-
我们要为Versatile Express架构进行编译,为此我们可以通过如下命令使用预配置文件“arch/arm/configs/vexpress_defconfig”:
make vexpress_defconfig
这将会配置编译器为期望的硬件,通过创建一个包含所有相关选项的名为”.config”的文件。然后,编译内核镜像的命令非常简单:
make all
在编译的最后会生成一个能够在RAM中自动解压的压缩的内核镜像文件”linux-3.2/arch/boot/zImage”。为了在多核主机上加速编译的过程,我建议通过执行如下命令来尝试平行编译:
make –j 2 all
这时将使用2个进程的平行编译来创建最后的镜像文件,而不再是顺序编译。
为了使内核做点儿什么,我们创建一个简单的”Hello World!”用户空间程序。我们可以使用一个内存磁盘作为Linux用来root的文件系统,使用”initfamfs”条目。关于内存磁盘的更多信息可以在内核源码树中找到,在文件“Documentation/early-userspace/README“。Linux尝试运行的第一个程序是”/init”,所以我们可以创建一个名称为此的可执行文件。源码非常简单:
1 2 3 4 5 6 |
#include
void main() { printf("Hello World!\n"); while(1); } |
它将会被我们的交叉编译工具链编译。为了使该程序单独工作,我们需要把它编译为一个静态可执行文件,将会把它所需要的库文件也包含进去。这种方式我们可以得到只有一个不用担心动态库的可执行程序作为文件系统。编译的命令是:
arm-linux-gnueabi-gcc -static init.c -o init
此命令将会为ARM创建一个名为init的可执行文件。这个文件的格式如下:
$file init
init:ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, forGNU/Linux 2.6.18, BuildID[sha1]=0xf78b4955773f3c5dfecdb11c62dae094a99ba8f9, notstripped
现在我们可是使用cpio应用程序创建我们的内存磁盘,仅仅把init文件放入一个归档包:
echoinit|cpio –o –format=newc > initramfs
注意这个命令必须在和init文件相同的目录中运行,initramfs文件就是我们的内存磁盘,你可以通过如下命令检查它的内容:
cpio–t 所有需要在Linux中通过QEMU执行的东西都齐备了,我们可以使用”-Mvexpress A9”选项来模拟VersatileExpress平台。zImage内核和initramfs镜像通过”-kernel”和”-initrd”选项被QEMU加载至模拟RAM中,指向相应的文件。 我们还想把启动信息送到控制台进行显示,为了在图形窗口中显示,我们需要传递”console=tty1”内核参数。这个内核参数将会被QEMU通过”-append”选项传递给Linux。 完整的命令如下: qemu-system-arm-M vexpress-a9 -kernel linux-3.2/arch/arm/boot/zImage -initrd initramfs -append"console=tty1" 这个命令将会打开QEMU并打开一个黑色的控制台窗口,通过一个Tuxlogo来显示图形能力。启动信息将会在这个图形窗口显示,并且在启动信息的最后会打印出我们的”HelloWorld!”信息。 另一方面,QEMU可以重定向主机上的模拟系统的串口,使用选项”-serialstdio”,则Linux可以通过传递”console=ttyAMA0”作为内核参数而在第一个串口中显示它的信息。则命令变为: qemu-system-arm-M vexpress-a9 -kernel linux-3.2/arch/arm/boot/zImage -initrd initramfs -serialstdio -append "console=ttyAMA0" 这个命令将会打开QEMU并打开一个黑色的控制台窗口,而启动信息会显示在主机的终端而非那个黑色的窗口。注意到”ttyAMA0”是一个依赖于其要模拟的硬件的串口名称,可能并不是所有的系统都是相同的。 在我的测试中,我使用的是QEMU1.0版本,如果你使用一个不同的版本,结果可能有所不同。5.模拟内核启动和内存磁盘运行