我的宿主机操作系统是RedHat9.0(建议安装的时候选择“完全安装”)
一个嵌入式Linux系统从软件的角度来看,分为四个层次:
Boot Loader. 包括固化在固件(firmware)中的 boot 代码(可选),和 Boot Loader 两大部分。
Linux kernel. 特定于嵌入式板子的定制内核以及内核的启动参数。
File system. 包括根文件系统和建立于 Flash 内存设备之上的文件系统。通常用 ramdisk 来作为root fs。
User application. 特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:MicroWindows 和 MiniGUI 等等。
以下是开发环境搭建过程中涉及到的知识点
一. minicom的使用
minicom是Linux OS下功能强大的串口终端程序。具体使用方法在此不详述,有很多相关资料可供查阅。
NOTE:在minicom的设置中,波特率要设置正确(否则,终端上是乱码),软件流控制和硬件流控制都应设置为“无”或“NO”。
minicom的设置被保存到文件/etc/minirc.dfl中。
进入有颜色的minicom界面:# minicom -m8 -c on
二. 配置tftp服务器
在这里,我是通过网络下载kernel image和root fs到开发板的。
tftp服务器是由超级守护进程xinetd运行的,这使得tftp服务器的配置操作比独立运行守护进程的服务器(如 vsftpd)简单得多。
一般,修改文件/etc/xinetd.d/tftp,当然,只有安装了tftp服务器,才会有这个文件。把“disable = yes”改成“disable = no”,然后保存,退出。重新启动超级守护进程xinetd,# service xinetd restart.把要下载的文件放到根目录下的tftpboot子目录中就ok了。
NOTE:在控制台终端输入“setup”,选中system services,回车进入系统服务选项菜单,选中[*] tftp(如果没有tftp服务,说明RedHat9.0安装的时候没有选择tftp服务,可以去安装包映像或安装光盘中找到相应的rpm包安装,ok),保存,退出setup界面。
三. 建立NFS文件系统
NFS是由SUN公司发展出来的分散式文件系统。可以让用户通过连接的网络,将其他电脑所共享的文件目录,映射到自己的系统下。用户在操作这些文件和目录时,感觉如同存储在本机上一样。共享出目录的一方称为NFS服务端,另一端称为NFS客户端。这里,当然是在开发板和宿主机之间建立NFS共享。
NOTE:首先确认宿主机上安装了NFS(查看目录/etc/xinetd.d/下是否存在nfs文件)。
NFS服务器端操作如下:
配置宿主机的以太网口
# ifconfig eth0 down
# ifconfig eth0 192.168.1.1 up
在宿主机上建立目录/home/emdeder/nfs
编辑/etc/exports文件如下:
/home/embeder/nfs 192.168.1.2(rw,sync,no_root_squash)
允许IP为192.168.1.2的ARMer9开发系统将宿主机上的/home/embeder/nfs这个目录mount到开发系统下,即,ARMer9开发系统可以通过网络访问宿主机上的/home/embeder/nfs目录。
执行/etc/init.d目录下的程序nfs,重启NFS Server
# cd /etc/init.d
# exportfs -rav
# ./nfs restart
NFS客户端的操作如下:
启动ARMer9开发系统上的Linux系统。
配置ARMer9开发系统上的以太网口
# ifconfig eth0 down
# ifconfig eth0 192.168.1.2 up
ping一下宿主机,看ARMer9开发系统和宿主机的网络是否相通
# ping -c 5 192.168.1.1
在ARMer9开发系统中建立目录/tmp/nfs
# mkdir /tmp/nfs
若相通,则将宿主机上的目录/home/embeder/nfs mount到ARMer9开发系统下的目录/tmp/nfs上。
# mount -t nfs -o nolock 192.168.1.1:/home/embeder/nfs /tmp/nfs
进入目录/tmp/nfs,查看该目录下的内容,是否和宿主机上的目录/home/embeder/nfs中的内容一样?^_^,应该是一样的。
下面就可以用这种方式在开发板上调试自己的应用程序了。
三. 编译内核
这里,我已经有了目标开发系统的Linux移植源代码,否则,移植是一项烦杂的事情,有时间的话,这也是一件很值得尝试的事情。
编译内核的软件环境是KBuild系统,泛指构建一个完整并能够运行的Linux内核所需要的一切资源。这些资源包括构建程序,脚本,中间件,配置文件和Makefile。依次进行以下操作:
# make menuconfig 进入Linux内核配置界面。
NOTE:在block device中选上ramdisk的支持并且根据需要修改ramdisk的默认大小,并且选上initrd的支持。还有,在file system中选上自己文件系统所用格式的相应支持(这里是ext3),否则,kernel将不能正确引导文件系统(一般出现kernel panic提示信息)。
我在这里静态编译进了目标开发板的声卡驱动,即Philip uda1341,在sound选项里面,当然,也可以用module的方式编译。
# make dep
kbuild调用中间件Script/mkdep来生成描述与核心文件形成依赖关系的.h文件(绝大多数)列表。这个列表保存在 .depend 中。用户即使不执行这一步,kbuild也会执行的。
# make clean
清除上次编译生成的 .o 文件。
# make zImage
构建内核映像
# make modules
编译被配置为模块的内核组件
# make modules_install
把编译好的内核模块按照功能copy到/lib/modules/2.4.23目录下
编译后,可安装模块将被存放的宿主机目录是:
/tmp/lib/modules/2.4.18-rmk7-pxa1/
驱动模块所在目录 /tmp/lib/modules/2.4.18-rmk7-pxa1/kernel/driver
四. kernel和ramdisk的U-Boot可启动映像文件的制作
uboot源代码的 tools/ 目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
mkimage在制作可启动映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录 mkimage 的参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载到内存中的哪个位置,入口点在内存中的哪个位置以及映象名是什么。
root@Glym:/tftpboot# ./mkimage
Usage: ./mkimage -l image
-l ==> list image header information
./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)
参数说明:
-A 指定CPU的体系结构:
取值 表示的体系结构
alpha Alpha
arm ARM
x86 Intel x86
ia64 IA64
mips MIPS
mips64 MIPS 64 Bit
ppc PowerPC
s390 IBM S390
sh SuperH
sparc SPARC
sparc64 SPARC 64 Bit
m68k MC68000
-O 指定操作系统类型,可以取以下值:
openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、
dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos
-T 指定映象类型,可以取以下值:
standalone、kernel、ramdisk、multi、firmware、script、filesystem
-C 指定映象压缩方式,可以取以下值:
none 不压缩
gzip 用gzip的压缩方式
bzip2 用bzip2的压缩方式
-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载
-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)
-n 指定映象名
-d 指定制作映象的源文件
例:制作PowerPC体系结构,vxworks操作系统的不压缩kernel映象,准备下载到内存地址0x800000位置,映象名称为vxworks.image:
root@Glym:/tftpboot# ./mkimage -A ppc -O vxworks -T kernel -C none -a 0x800000 -e 0x800040 -n vxworks.image -d vxWorks image
最后会生成一个文件名称为image的映象文件,这个文件就是我们所需要的了。请注意-n参数中指定映象名不是生成的映象文件地文件名,映象名是记录在0x40个字节的头部信息中的。
五. 用U-Boot下载Linux kernel和root fs到目标开发板(在不同的开发板上,下载地址可能不同)。
# setenv ipaddr 192.168.1.2
# setenv serverip 192.168.1.1 (确保开发板和宿主机在同一网段)
# tftpboot 0x30007fc0 uImage.s3c2410-f
# tftpboot 0x30800000 ramdisk.u-boot-f
# bootm 0x30007fc0 0x30800000
如果要下载档案并且写入到NOR Flash中,方法如下:
把uImage.s3c2410-f通过tftp download到SDRAM中的0x30007fc0的位置
SMDK2410> tftpboot 0x30007fc0 uImage.s3c2410-f
将0x30007fc0位置开始长度为0x100000(即1M,这个长度取决于uImage.s3c2410-f的大小,比uImage.s3c2410-f的大小要大,在256K字节的边界上即可)的字节烧写到flash中(地址为0x200000)
SMDK2410> cp.b 0x30007fc0 0x200000 0x100000
把ramdisk.u-boot-f通过tftpdownload到SDRAM中的0x30800000 的位置
SMDK2410> tftpboot 0x30800000 ramdisk.u-boot-f
将0x30007fc0位置开始长度为0x1000000(即16M,这个长度取决于ramdisk.u-boot-f的大小,比ramdisk.u-boot-f的大小要大,在256K字节的边界上即可)的字节烧写到flash中(地址为0x800000)
SMDK2410> cp.b 0x3080000 0x800000 0x1000000
这时候,linux kernel和ramdisk已经保存到flash的0x200000和0x800000这两个位置了。
如果不需要每次起来自动启动linux,就在复位后进入u-boot命令界面,输入下面的命令即可:
SMDK2410> cp.b 0x200000 0x30007fc0 0x100000
SMDK2410> cp.b 0x800000 0x30800000 0x1000000
SMDK2410> bootm 0x30007fc0 0x30800000
如果想每次复位后自动启动linux,就把上面3条命令通过setenv命令设置到u-boot的环境变量bootcmd中。
SMDK2410>setenv bootcmd cp.b 0x200000 0x30007fc0 0x100000; cp.b 0x800000 0x30800000 0x1000000; bootm 0x30007fc0 0x30800000
SMDK2410>protect off 0x00040000 0x0007ffff
SMDK2410>saveenv
SMDK2410>protect on 0x00040000 0x0007ffff
六. 基于s3c2410嵌入式处理器的开发板上,mp3播放程序madplay编译总结
1. 编译zlib,因为libid3tag需要这个库
# ./configure --prefix=/opt/host/armv4l/
修改Makefile
AR=/opt/host/armv4l/bin/armv4l-unknown-linux-ar
CC=/opt/host/armv4l/bin/armv4l-unknown-linux-gcc
RANLIB=/opt/host/armv4l/bin/armv4l-unknown-linux-ranlib
最后AR命令要添加rcs,否则会出错
具体操作:
libz.a : $(OBJS)$(OBJA)
$(AR)rcs$@$(OBJS)$(OBJA)
-@($(RANLIB)$@||true)>/dev/null2>&1
# make
# make install
2. 编译libid3tag
# ./configure --host=armv4l-unknown-linux
CC=/opt/host/armv4l/bin/armv4l-unknown-linux-gcc
--disable-debugging --disable-shared
--prefix-/opt/host/armv4l/
# make
# make install
3. 编译libmad
# ./configure --enable -fpm=arm --host=armv4l-unknown-linux
--disable-shared --disable-debugging
--prefix=/opt/host/armv4l/
CC=/opt/host/armv4l/bin/armv4l-unknown-linux-gcc
# make
# make install
4. 编译madplay
# ./configure --host=armv4l-unknown-linux
CC=/opt/host/armv4l/bin/armv4l-unknown-linux-gcc
--disable-debugging
--disable-shared
CPPFLAGS='-I/opt/host/armv4l/include'
LDFLAGS='-L/opt/host/armv4l/lib'
# make
NOTE:可以在Makefile里加上 -static 选项,从而得到静态链接的程序。
另外,madplay官方主页是:www.underbit.com
zlib下载地址是:http://www.zlib.net,当前版本是zlib 1.2.3 (2005.7)
七.root文件系统的制作
Ramdisk是分配了一部分内存作为一个分区来使用。对于系统运行时不断使用的程序,将它们放在Ramdisk中将加快计算机的操作,如大数据量的网络服务器、无盘工作站等。为了能够使用Ramdisk,我们在编译内核时须将block device中的Ramdisk支持选上,它下面还有两个选项,一个是设定Ramdisk的大小,默认是4096k;另一个是initrd的支持。它既可以直接编译进内核,也可以编译成模块,在需要的时候加载。如果在启动时就用它(例如加载ramdisk作为rootfs),就必须将它直接编译进内核。
在Linux下建立目录/myramdisk,将在此目录下创建的文件系统对应的文件结构。一般不需要对于系统运行是完全无用的文件,如帮助手册、信息页,头文件、内核源码等。
General:要建立的目录是,dev,设备;etc,系统配置文件;sbin,重要的系统程序;bin,基本应用程序;lib,共享函数库;mnt,装载其他磁盘节点;usr,附加应用程序;proc,proc文件系统;其中,proc,mnt,usr可以是空的。
一些要包含在 Ramdisk 中的重要目录是:
/bin — 保存大多数如 init、busybox、shell、文件管理实用程序等二进制文件;
/sbin — 保存系统启动过程通常需要的命令,如e2fsck、mke2fs、fdisk、insmod、rmmod、depmod、modprobe、lsmod、shutdown、reboot、login、init、getty、mount、umount、等;
/dev — 包含用在设备中的所有设备节点,一定要有的是console,kmem,mem,null,ram,tty* 等文件;
/etc — 包含必不可少的系统配置文件,一般包含rc.d/* (系统启动脚本),fstab(列出要登陆的文件系统),inittab(包含启动过程参数),passwd(用户名和目录),group(用户组),shadow(用户加密密码)。如果不使用init作为登陆进程,而将init链接到/bin/bash,即,#ln –s /bin/bash /sbin/init,则/etc目录下可以不含任何文件,这种情况类似单用户登陆系统,同样,也不需要输入用户名和密码;
/proc — 这是一个必须设置的特殊目录,在系统运行之后它下面有许多内容,这些内容是实时、不断跟踪系统内核和正在运行的进程的状态而产生的,但不占用任何磁盘空间,而是驻留在内存中;在某些情况下,可以通过它来进行系统设置,许多工具从这里获取信息,如dmesg、ps、top等;在内核编译选项文件系统选择中,我们选择对文件系统proc的支持;
/lib — 包含所有必需的库,根据/bin和/sbin目录下的可执行文件需要的动态链接库来确定lib目录下的内容,其中必须有函数装载器,或是ld.so(对a.out库)或是ld-linux.so(对ELF库)。
文件系统制作完成,看看这个/myramdisk目录大小为多大,然后确定需要多大的Ramdisk,在内核编译的时候,修改Ramdisk大小(缺省为4096k)为所需要的尺寸。
现在我们以制作一个4M的Ramdisk为例子,步骤如下:
1. 在宿主机上编译运行在目标机linux内核时,将block device中的Ramdisk支持选上,设定Ramdisk的大小为缺省的4096K,对initrd的支持也选上。
2. 在宿主机上建立/myramdisk目录,按照上述的原则建立必要的目录和文件。
3. 执行下列步骤:
在根目录下建立一个文件 /images/initrd.img,从设备文件/dev/zero中读取数据填充文件initrd.img(NOTE:/dev/zero是一个特殊的设备文件,从这个文件读出来的数据全是0,以便后面得到更高的压缩率);
# mkdir /images
# dd if=/dev/zero of=/images/initrd.img bs=1k count=4096
在Ramdisk上建立4M的ext2类型的文件系统;
# mke2fs -m0 -F /images/initrd.img 4096
也可以是:# mke2fs -F -v -m0 /images/initrd.im
若要建立ext3类型的filesystem,命令是:
# mke2fs -j -m0 -F /images/initrd.img 4096 ,同样,也可以不指定大小)
在/mnt下建立目录rootfs, 将已经格式化为ext2类型文件系统的ramdisk挂载至目录/mnt/rootfs上;
# mkdir –p /mnt/rootfs
# mount –o loop /images/initrd.img /mnt/rootfs
将宿主机上/myramdisk目录下的文件结构拷贝至/mnt/rootfs目录中(这就相当于拷贝至文件initrd.img中了);
# cp -av /myramdisk/* /mnt/rootfs
(NOTE:必须用命令拷贝的方式,否则可能出错)
卸载掉/mnt/rootfs;
# umount /mnt/rootfs
产生一个压缩的映象文件。
# gzip -9 < images/initrd.img > images/initrd.bin
最后,用mkimage工具将其制作成U-Boot的可执行ramdisk映像
# ./mkimage -n 'RAM disk' -A arm -O linux -T ramdisk -C gzip -d initrd.bin initrd.boot
也可以是:
# ./mkimage –A arm –O linux –T ramdisk –C gzip –a 0x30800000 –e 0x30800000 –n ‘ramdisk’ –d initrd.bin initrd.boot