1.1 构建嵌入式Linux交叉开发环境
构建开发环境是任何开发工作的基础,对于软、硬件非常丰富的嵌入式系统来说,构建高效、稳定的环境是能否开展工作的重要因素之一。本节将介绍如何构建一套嵌入式Linux开发环境。在构建开发环境之前,有必要了解嵌入式Linux开发流程。因为嵌入式Linux开发往往会涉及多个层面,这与桌面开发有很大不同。构建一个Linux系统,须仔细考虑下面几点:
- 选择嵌入式Linux发行版。商业的Linux发行版是作为产品开发维护的,经过严格的测试验证,并且可以得到厂家的技术支持。它为开发者提供了可靠的软件和完整的开发工具包。
- 熟悉开发环境和工具。交叉开发环境是嵌入式Linux开发的基本模型。Linux环境配置、GNU工具链、测试工具甚至集成开发环境都是开发嵌入式Linux的利器。
- 熟悉Linux内核。因为嵌入式Linux开发一般需要重新定制Linux内核,所以熟悉内核配置、编译和移植很重要。
- 熟悉目标板引导方式。开发板的Bootloader负责硬件平台最基本的初始化,并且具备引导Linux内核启动的功能。由于硬件平台是专门定制的,一般需要修改编译Bootloader。
- 熟悉Linux根文件系统。高级一点的操作系统一般都有文件系统的支持,Linux也一样离不开文件系统。系统启动必需的程序和文件都必须放在根文件系统中。Linux系统支持的文件系统种类非常多,我们可以通过Linux内核命令行参数指定要挂接的根文件系统。
- 理解Linux内存模型。Linux是保护模式的操作系统。内核和应用程序分别运行在完全分离的虚拟地址空间,物理地址必须映像到虚拟地址才能访问。
- 理解Linux调度机制与进程和线程编程。Linux调度机制影响到任务的实时性,理解调度机制可以更好地运用任务优先级。此外,进程和线程编程是应用程序开发所必需的。
1.1.1 搭建嵌入式交叉编译环境
搭建交叉编译环境是嵌入式开发的第一步,也是关键的一步。不同的体系结构、不同的操作内容甚至是不同版本的内核,都会用到不同的交叉编译器。选择交叉编译器非常重要,有些交叉编译器经常会有部分的BUG,都会导致最后的代码无法正常运行。
交叉编译器完整的安装一般涉及多个软件的安装(读者可以从ftp://gcc.gnu.org/pub/下载),包括binutils、gcc、glibc、glibc-linuxthreads等软件。其中,binutils主要用于生成一些辅助工具,如readelf、objcopy、objdump、as、ld等;gcc是用来生成交叉编译器的,主要生成arm-linux-gcc交叉编译工具(应该说,生成此工具后已经搭建起交叉编译环境,可以编译Linux内核了,但由于没有提供标准用户函数库,用户程序还无法编译);glibc主要是提供用户程序所使用的一些基本的函数库,glibc-linuxthreads是线程相关函数库。这样,交叉编译环境就完全搭建起来了。
上面所述的搭建交叉编译环境比较复杂,很多步骤都涉及对硬件平台的选择。因此,现在嵌入式平台社区或厂商一般会提供在各种平台上测试通过的交叉编译器,或把以上安装步骤全部写入脚本文件或者以发行包的形式提供,这样就大大方便了用户的使用。
在本书中采用广泛使用的cross-4.3.2交叉编译器工具链,其使用方法非常简单。
$ mkdir -p /usr/local/arm /* 这是交叉编译器安装目录*/ $ cp cross-4.3.2.bar.bz2 /usr/local/arm $ cd /usr/local/arm $ tar jxvf cross-4.3.2.tar.gz
此时在/usr/local/arm/4.3.2/bin/下已经出现了很多交叉编译工具,显示如下:
arm-none-linux-gnueabi-addr2line arm-none-linux-gnueabi-gfortran arm-none-linux-gnueabi-ar arm-none-linux-gnueabi-gprof arm-none-linux-gnueabi-as arm-none-linux-gnueabi-ld arm-none-linux-gnueabi-c++ arm-none-linux-gnueabi-ldd arm-none-linux-gnueabi-cc arm-none-linux-gnueabi-nm arm-none-linux-gnueabi-c++filt arm-none-linux-gnueabi-objcopy arm-none-linux-gnueabi-cpp arm-none-linux-gnueabi-objdump arm-none-linux-gnueabi-ct-ng.config arm-none-linux-gnueabi-populate arm-none-linux-gnueabi-g++ arm-none-linux-gnueabi-ranlib arm-none-linux-gnueabi-gcc arm-none-linux-gnueabi-readelf arm-none-linux-gnueabi-gcc-4.3.2 arm-none-linux-gnueabi-run arm-none-linux-gnueabi-gccbug arm-none-linux-gnueabi-size arm-none-linux-gnueabi-gcov arm-none-linux-gnueabi-sstrip arm-none-linux-gnueabi-gdb arm-none-linux-gnueabi-strings arm-none-linux-gnueabi-gdbtui arm-none-linux-gnueabi-stri
可以看到,这个交叉编译工具集成了binutils、gcc、glibc这几个软件,而每个软件也都有比较复杂的配置信息。
接下来,在环境变量PATH中添加路径,就可以直接使用arm-none-linux—gnueabi-gcc命令了。
$ export PATH=$PATH:/usr/local/arm/4.3.2/bin
把交叉开发工具链的路径添加到环境变量PATH中,这样可以方便地在Bash或者Makefile中使用这些工具。通常环境变量的配置文件有如下几个。
- profile类文件:用户第一次登录时仅运行一次,profile类文件包括每个用户主目录下的.profile文件和/etc/profile等。用户再次登录时就会运行主目录下的.profile文件的脚本。
- bashrc类文件:每当打开bash shell时(如当打开一个虚拟终端时)运行该脚本文件。bash类文件包括每个用户主目录下的.bashrc文件和/etc/bash.bashrc等。把环境变量配置的命令添加到其中一个文件中即可。
$ arm-linux-gcc -v /*查看交叉编译器的版本信息*/ arm-none-linux-gnueabi-gcc -v Using built-in specs. Target: arm-none-linux-gnueabi Configured with: /home/linux/crosstooll/toolchain_build/targets/src/gcc-4.3.2/configure--build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=arm-none-linux-gnuea bi --prefix=/usr/local/arm/4.3.2 --with-sysroot=/home/linux/toolchain/arm-none-linux-gnueabi//sys-root --enable-languages=c,c++,fortran --disable-multilib --with-arch=armv4t --with-cpu= arm9tdmi --with-tune=arm920t --with-float=soft --with-pkgversion=crosstool-NG-1.8.1-farsight--disable-sjlj-exceptions --enable-__cxa_atexit --disable-libmudflap --with-gmp=/home/linux/c rosstooll/toolchain_build/targets/arm-none-linux-gnueabi/build/static--with-mpfr=/home/linux/crosstooll/toolchain_build/targets/arm-none-linux-gnueabi/build/static--enable-threads=posi x--enable-target-optspace--with-local-prefix=/home/linux/toolchain/arm-none-linux-gnueabi//s ys-root --disable-nls --enable-symvers=gnu--enable-c99 --enable-long-long Thread model: posix gcc version 4.3.2
从上面打印的版本信息中可以看到“--prefix=/usr/local/arm/ 4.3.2”,这就是交叉编译器安装的路径。它是在编译前通过prefix选项配置的,所以,这个工具链安装的路径必须是/usr/local/arm/4.3.2。
1.1.2 配置主机交叉开发环境
1.配置控制台程序
要查看目标板的输出,可以使用控制台程序。在各种操作系统上一般都有现成的控制台程序可以使用,例如,Windows操作系统中有超级终端(HyperTerminal)工具;Linux/UNIX操作系统有minicom(使用“minicom”命令启动该软件)等工具。无论什么操作系统和通信工具,都可以作为串口控制台。如果在Windows平台上运行Linux虚拟机,这个串口通信软件可以任选一种。配置一个超级终端,如图1.1所示,配置minicom(使用“minicom -s”命令进入配置界面),如图1.2所示,配置参数包括串口号、通信速率、数据位数、停止位数、奇偶校验、数据流控制等设置。一次配置成功后可以将结果保存,供以后使用。
图1.1 配置串口控制台
图1.2 minicom配置
2.配置tftp服务
tftp是一个传输文件的简单协议,它基于UDP协议实现。此协议设计时是进行小文件传输的,因此它不具备通常的FTP的许多功能,只能从文件服务器上获得或写入文件,不能列出目录,不进行认证,只能传输8位数据。
tftp服务分为客户端服务和服务器端服务两种。通常,首先在宿主机上开启tftp服务器端服务,设置好tftp的根目录内容(也就是供客户端下载的文件),然后,在目标板上开启tftp的客户端程序(tftp客户端主要在Bootloader交互环境下运行,几乎所有Bootloader都提供该服务,用于下载操作系统内核和文件系统)。这样,把目标板和宿主机用直连线相连之后,就可以通过tftp协议传输可执行文件。下面分别讲述在Linux下和Windows下的配置方法。
1)Linux下的tftp服务配置
Linux下tftp的服务是由xinetd(还有openbsd-inetd等其他服务)所设定的,默认情况下是处于关闭状态。
首先,要修改tftp的配置文件,开启tftp服务,如下所示:
$ vim /etc/xinetd.d/tftp service tftp { socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /tftpboot disable = no per_source = 11 cps = 100 2 flags = IPv4 }
在这里,主要是要将“disable=yes”改为“disable=no”。另外,通过“server_args”可以看出,tftp服务器端的默认根目录为“/tftpboot”,用户可以根据需要更改为其他目录。
接下来,重启xinetd服务,使刚才的更改生效,如下所示:
$ /etc/init.d/xinetd restart
然后,使用命令“netstat -au”以确认tftp服务是否已经开启,如下所示:
$ netstat -au | grep tftp Proto Recv-Q Send-Q Local Address Foreign Address State udp 0 0 *:tftp *:*
这时,用户就可以把所需的传输文件放到“/tftpboot”目录下,这样,主机上的tftp服务就建立起来。用网络交叉线把目标板和宿主机连起来,并且将其配置成一个网段的地址,再在目标板上启动tftp客户端程序(注意:不同的Bootloader所使用的命令会有所不同,读者可以查看帮助来获得确切的命令名及格式,本书以U-Boot为例讲解),如下所示:
# tftp 0x30008000 zImage TFTP from server 192.168.1.112; our IP address is 192.168.1.120 Filename 'zImage'. Load address: 0x33000000 Loading:########################################################### ########################################################### ########################################################### done Bytes transferred = 881988 (d7544 hex)
可以看到,此处目标板使用的IP为“192.168.1.120”,宿主机使用的IP为“192.168.1.112”,下载到目标板的地址为0x33000000,文件名为“zImage”。
2)Windows
在Windows下配置tftp服务需要使用tftp服务器软件,常见的有Tftpd32,读者可以自行从网上下载。要注意的是,该软件是tftp的服务器端,而目标板上则是tftp的客户端。打开该软件,如图1.3所示。
接下来,用户可以在Settings中配置服务器端的各个选项,如IP地址等,如图1.4所示。
另外,还需要在Browse中选择tftp的服务器端根目录。这时,tftpd会提示用户重启该软件,使修改的参数生效。至此,tftp的服务就配置完毕。此时可以用直连线连接目标机和宿主机,且在目标机上开启tftp服务进行文件传输。
图1.3 Tftpd32软件
图1.4 Tftpd32的配置界面
3.NFS文件系统
NFS为Network File System的简称,最早是由Sun公司提出发展起来的,其目的就是让不同的机器、不同的操作系统之间可以彼此共享文件。
NFS可以让不同的主机通过网络将远端的NFS服务器共享出来的文件安装到自己的系统中,从客户端看来,使用NFS的远端文件就像是使用本地文件一样。在嵌入式系统中使用NFS会使应用程序的开发变得十分方便,并且不用反复地烧写镜像文件。
NFS的使用分为服务器端和客户端,其中服务器端提供要共享的文件,而客户端则通过挂载“mount”这一动作来实现对共享文件的访问操作。在嵌入式开发中,通常NFS服务端在宿主机上运行,而客户端在目标板上运行。
NFS服务器端是通过读入它的配置文件“/etc/ exports”来决定所共享的文件目录的,在这个配置文件中,每一行都代表一项要共享的文件目录,以及所指定的客户端对其的操作权限。客户端可以根据相应的权限,对该目录下的所有目录文件进行访问。
配置文件中每一行的格式如下:
[共享的目录] [客户端主机名称或IP]([参数1,参数2…])
在这里,主机名或IP是可供共享的客户端主机名或IP,若对所有的IP都可以访问,则可用“*”表示。这里的参数有很多种组合方式,表1.1列出了常见的参数。
表1.1 NFS配置文件的常见参数
下面是配置文件“/etc/exports”的一个示例:
$ cat /etc/exports /home/david/project *(rw,sync,no_root_squash)
在设定完配置文件之后,需要启动nfs服务和portmap服务,这里的portmap服务允许NFS客户端查看NFS服务所用的端口,在它被激活之后,就会出现一个端口号为111的sun RPC(远端过程调用)的服务,这是NFS服务中必须实现的一项,因此,也必须把它开启,如下所示:
$ /etc/init.d/portmap restart 启动 portmap: [确定] $ /etc/init.d/nfs restart(在Ubuntu中应为/etc/init.d/nfs-kernel-server) 启动 NFS 服务: [确定] 关掉 NFS 配额: [确定] 启动 NFS 守护进程: [确定] 启动 NFS mountd: [确定]
可以看到,系统在启动NFS服务时就已经启动了mountd进程,它是NFS挂载服务,用于处理NFSD递交过来的客户端请求。另外还会激活至少两个以上的系统守护进程,然后开始监听客户端的请求,用dmesg命令(或者cat /var/log/messages)可以看到操作是否成功。另外,与NFS相关的还有两个命令,可以方便NFS的使用。
其一是exportfs,它可以重新扫描“/etc/exports”,使用户在修改“/etc/exports”配置文件时不需要每次重启NFS服务,其格式为:
exportfs [选项]
表1.2所示为exportfs的常见选项。
表1.2 exportfs的常见选项
另一个是showmount,它可以显示nfs服务器的挂载信息,其格式为:
showmount [选项]
表1.3所示为showmount的常见选项。
表1.3 showmount的常见选项
用户若希望NFS服务在每次系统引导时自动开启,可使用以下命令:
# /sbin/chkconfig nfs on (在Ubuntu中应该输入 /sbin/chkconfig nfs-kernel-server on)