uClinux的编译与运行详解
按常规编译完成后应该生成
image.bin linux linux.data linux.text
romfs.img这5个文件,不过我只有linux/boot.rom(=romfs.img),今天上网查了查它们的定义。
romfs.img:这个文件是通过tools/romfs-inst.sh脚本,生成romfs目录及其下面的文 件,然后通过genromfs程序打包成一个文件romfs.img。romfs.img既可以放在Flash中,也可以放在RAM中,但需要在编译内核 时在driver/block/blkmem.c里指定地址,以便内核找到。
linux.text:编译后的内核中text段,一般放在Flash中,只读。
linux.data:编译后的内核中data、init段,一般放在SDRAM中,读写。
image.rom:通常image.rom的文件大小要比image.ram小很多,这是因为image.rom是 一个压缩过的内核,前面加上一个小的解压程序负责把内核解压后搬到指定位置。这个文件可以直接烧进Flash中,当然也可以在RAM中运行。这个文件实际 上就是通常Linux生成的zImage文件。
image.ram:这个内核没有压缩过,而且必须在RAM里运行。所以需要通过板子上的Bootloader将它下 载到指定位置后开始执行。相对image.rom而言,从RAM里启动内核,则代码段和数据段都在RAM里面。这个文件是编译生成的ELF格式的 Linux内核,通过arm-elf-objcopy工具生成的二进制映像文件。可以这样形象的说:image.rom = gunzipprogram + gzipped(image.ram)。
boot.rom:其实就是uClinux编译完成后的文件images/romfs.img,可以看一下它就是一个连接。
image.bin:上面三个文件顺序连接而生成的,image.bin = linux.text + linux.data + romfs.img。
1.创建开发环境
首先需要编译的环境。建议最好使用Linux 作为开发平台,因为Linux 以及uClinux 的开发者们基本都是在Linux 下完成的开发。为了能够编译uClinux,我们需要下载uClinux 的编译器。这个文件可以在uClinux 的官方网站得到,也可以在论坛的客服中心下载。
官方的下载地址是:
http://www.uclinux.org/pub/uClinux/arm-elf-tools/arm-elf-tools-20040427.sh
虽然同为gcc 和binutils 工具,但是必须使用uClinux 发布的版本,因为这个gcc 和binutils 都是为了能够编译FLAT 格式的文件而订制的,另外也加入了ARM 对PIC(Position Independent Code,位置无关代码)的支持。PIC 的支持是用来编译动态库或者XIP 模式的可执行文件。关于XIP 的介绍有一篇文章: http://www.ucdot.org/article.pl?sid=02/08/28/0434210
大家可以了解一下XIP。但是由于XIP 并不是很稳定,所以在我们发布的uClinux 版本中并没有使用这个技术。然后,将下载得到的文件,是一个sh 文件。这是一个自解压的文件(就好比Windows下面的自解压zip 或者rar 一样)。为了能够运行,我们需要:
./arm-elf-tools-20040427.sh
这样如果不能运行的话,那应该使用下面的命令:
chmod u+x arm-elf-tools-20040427.sh(使之可执行)
./arm-elf-tools-20040427.sh
得到了编译环境,就可以编译源代码了。当然,首先要把代码解压缩到你的目录下面:
tar xjvf uClinux-dist-20060803.tar.bz2
它会把全部的文件解压缩到uClinux-dist 的目录下。然后进入目录
cd uClinux-dist
2.编译uClinux
对uClinux 进行配置。那是通过 make menuconfig 或者make xconfig 来实现的。对于编译uClinux,不能简单地通过make 来实现。我们需要有一些特定的步骤才能保证编译的正确。这是因为uClinux 所需要支持的硬件平台太多了,不能考虑的很周到。为了编译最后得到的镜像文件,我们需要linux 的内核以及romfs。对于 GDB/ARMulator的移植来说,romfs 是被编译到内核里面去的。因此,在编译内核前需要一个romfs。为了得到romfs 的image,我们又需要编译用户的应用程序。而为了编译用户的应用程序,我们又需要编译C 运行库,这里我们用的C 运行库是 uClibc。根据上面的分析,我们编译uClinux 的步骤如下: make dep
这个仅仅是在第一次编译的时候需要,以后就不用了,为的是在编译的时候知道文件之间的依赖关系,在进行了多次得编译后,make 会根据这个依赖关系来确定哪些文件需要重新编译、哪些文件可以跳过)。
make lib_only
编译uClibc。以后我们编译用户程序的时候需要这个运行库,在运行这步时,采用uC-libc出错,但发现有些人却用的是这个,没问题,不知为何。
make user_only
编译用户的应用程序,包括初始化进程init,和用户交互的bash,以及集成了很多程序的busybox(这样对一个嵌入式系统来说可以减少存放的空 间,因为不同的程序共用了一套C 运行库),还有一些服务,如boa(一个在嵌入式领域用的很多的Web 服务器)和telnetd(telnet 服务器,我们可以通过网络来登录我们的uClinux 而不一定使用串口)。
make romfs(容易出错,见UC移植)
在用户程序编译结束后,因为我们用到的是romfs(一种轻量的、只读的文件系统)作为uClinux 的根文件系统,所以首先需要把上一步编译的很多应用程序以uClinux 所需要的目录格式存放起来。原来的程序是分散在user 目录下,现在例如可执行文件需要放到bin目录、配置文件放在etc 目录下..这些事就是make romfs 所做的。它会在uClinux 的目录下生成一个romfs 目录并且把user 目录下的文件、以及vendors 目录下特定系统所需要的文件(我们的vendors 目录是vendors/ARMlator/ARMulator组织起来,以便下面生成romfs 的单个镜像所用。
make image(容易出错)
它的作用有2 个,一个是生成romfs 的镜像文件,另一个是生成Linux 的镜像。因为原来的Linux 编译出来是elf 格式的,不能直接用于下载或者编译(不过那个文件也是需要的,因为如果你需要,那个elf 格式的内核文件里面可以包含调试的信息)。因此在这个时候由于还没有编译过Linux,因此在执行这一步的时候会报错。但是没有关系,因为我们在这里需要 的仅仅是romfs 的镜像,以便在下面编译Linxu 内核的时候使用。
make linux
(可能会出现error:cannot open romfs.o,其实这个文件已经有了,在linux-2.4.x/romfs/下,copy到uC下就可以了)
有了romfs 的镜像我们就可以编译Linux 了。因为我们的romfs 是嵌入到linux 内核中去了,所以在编译Linux 内核的时候就要一个romfs.o 文件。这个文件是由上面的make image生成的。
make image
(可能images/下只有一个boot.rom,我就是,其实这个文件就是romfs.img啦,链接一下,ln -s boot.rom romfs.img)
这里再一次make image 就是为了得到uClinux 的可执行文件的镜像了。执行了这一步之后,就会在images 目录下找到3 个文件:image.ram,image.rom,romfs.img。其中,image.ram和image.rom 就是我们需要的镜像文件。这个在下面“使用uClinux”会提到。为了方便起见,在uClinux-dist 目录下有两个文件:build_first.sh 和build_final.sh 先执行build_first.sh 再执行build_final.sh 就可以得到所需的镜像文件。之所以没有把两个合并,是因为第一次使用make image 会出错。
3.使用uClinux
上面我们提到了两个文件:image.ram 和image.rom。其中,image.ram 是直接下载到RAM 执行的文件。如果你还处于调试阶段,那么就没有必要把文件烧写到FLASH 里面。这个时候我们可以使用image.ram。在开发板运行BIOS 的情况下,使用:
netrun
将文件下载到RAM 并且自动执行。具体可以参考BIOS 的教程。
对于image.rom 来说,它是一个zImage 文件,也就是自解压的内核。由于它使用了gzip将内核压缩过,所以可以减小文件的大小。这个image 应该烧写到FLASH 的0x10000 的位置,而不能直接下载到RAM 并执行。在开发板运行了uClinux 之后,我们可以通过串口看到很多输出,但是最后可以看到
Welcome to
____ _ _
/ __| ||_|
_ _| | | | _ ____ _ _ _ _
| | | | | | || | _ /| | | |/ // /
| |_| | |__| || | | | | |_| |/ /
| ___/____|_||_|_| |_|/____|/_//_/
| |
|_|
Execution Finished, Exiting
Sash command shell (version 1.1.1)
/>
这样就表明你的uClinux 正常启动了。这时候可以输入一些命令,例如:
/> ls -l
drwxr-xr-x 1 0 0 32 Jan 1 00:00 bin
drwxr-xr-x 1 0 0 32 Jan 1 00:00 dev
drwxr-xr-x 1 0 0 32 Jan 1 00:00 etc
drwxr-xr-x 1 0 0 32 Jan 1 00:00 home
drwxr-xr-x 1 0 0 32 Jan 1 00:00 lib
drwxr-xr-x 1 0 0 32 Jan 1 00:00 mnt
dr-xr-xr-x 16 0 0 0 Jan 1 00:00 proc
lrwxrwxrwx 1 0 0 97 Jan 1 00:00 sbin -> /bin
lrwxrwxrwx 1 0 0 105 Jan 1 00:00 tmp -> /var/tmp
drwxr-xr-x 1 0 0 32 Jan 1 00:00 usr
drwxr-xr-x 8 0 0 1024 Jan 1 00:00 var
/>
ls 命令可以列出当前目录下的文件,-l 参数可以列出更详细的信息。
/> cat /proc/meminfo
total: used: free: shared: buffers: cached:
Mem: 14630912 2199552 12431360 0 499712 507904
Swap: 0 0 0
MemTotal: 14288 kB
MemFree: 12140 kB
MemShared: 0 kB
Buffers: 488 kB
Cached: 496 kB
SwapCached: 0 kB
Active: 744 kB
Inactive: 240 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 14288 kB
LowFree: 12140 kB
SwapTotal: 0 kB
SwapFree: 0 kB
/>
cat 可以将一个文件显示出来,这里的/proc/meminfo 表示内存的信息。您还可以看看
proc 目录下有些什么文件并且把他们显示出来看看
/> ps
PID PORT STAT SIZE SHARED %CPU COMMAND
1 S 134K 0K 0.1 init
2 S 0K 0K 0.0 keventd
3 S 0K 0K 0.0 ksoftirqd_CPU0
4 S 0K 0K 0.0 kswapd
5 S 0K 0K 0.0 bdflush
6 S 0K 0K 0.0 kupdated
15 S0 R 136K 0K 0.0 /bin/sh
16 S 70K 0K 0.0 /bin/inetd
17 S 264K 0K 0.0 /bin/boa
/>
ps 命令列出当前的进程的情况
在上面ps 的输出结果我们可以但到里面有boa,这就是我们的Web 服务器。另外,telnetd
是通过inetd 来启动的。
先来看一下boa。我们在主机的浏览器输入 http://192.168.111.100
就应该看见一个默认的页面:
然后我们也可以通过telnet 来访问我们的uClinux 机器:
在主机使用Telnet 工具登录到uClinux(IP 地址上面有了,是192.168.111.100)
它会要你登录,选择用户名root,密码为空,就可以进去了。
就如下图所示:
如果这时候用ps 查看的话,就会发现多出来了2 个进程:
PID PORT STAT SIZE SHARED %CPU COMMAND
1 S 134K 0K 0.0 init
2 S 0K 0K 0.0 keventd
3 S 0K 0K 0.0 ksoftirqd_CPU0
4 S 0K 0K 0.0 kswapd
5 S 0K 0K 0.0 bdflush
6 S 0K 0K 0.0 kupdated
15 S0 R 140K 0K 0.0 /bin/sh
16 S 70K 0K 0.0 /bin/inetd
17 S 268K 0K 0.0 /bin/boa
19 S 74K 0K 0.0 /bin/telnetd
20 p0 S 134K 0K 0.0 sh
下面的第19、20 号进程就是新的。也就是和telnet 有关的两个进程