uClinux上的应用程序设计Word文件下载.docx
《uClinux上的应用程序设计Word文件下载.docx》由会员分享,可在线阅读,更多相关《uClinux上的应用程序设计Word文件下载.docx(21页珍藏版)》请在冰点文库上搜索。
熟悉主流Linux的开发者会注意到在uClinux下工作的微小差异,但同样也可以很快熟悉uclinux的一些特性。
对于设计内核或系统空间的应用程序的开发者,要特别注意uClinux既没有内存保护,也没有虚拟内存模型,另外,有些内核系统调用也有差异。
1.1内存保护
没有内存保护(Memory Protection)的操作会导致这样的结果:
即使由无特权的进程来调用一个无效指针,也会触发一个地址错误,并潜在地引起程序崩溃,甚至导致系统的挂起。
显然,在这样的系统上运行的代码必须仔细编程,并深入测试来确保健壮性和安全。
对于普通的Linux来说,需要运行不同的用户程序,如果没有内存保护将大大降低系统的安全性和可靠性;
然而对于嵌入式uClinux系统而言,由于所运行的程序往往是在出厂前已经固化的,不存在危害系统安全的程序侵入的隐患,因此只要应用程序经过较完整的测试,出现问题的概率就可以控制在有限的范围内。
1.2 虚拟内存
没有虚拟内存(VirtualMemory)主要导致下面几个后果:
首先,由内核所加载的进程必须能够独立运行,与它们在内存中的位置无关。
实现这一目标的第一种办法是一旦程序被加载到RAM中,那么程序的基准地址就“固定”下来;
另一种办法是产生只使用相对寻址的代码(称为“位置无关代码”,PositionIndependentCode,简称PIC)。
uClinux对这两种模式都支持。
其次,要解决在扁平(flat)的内存模型中的内存分配和释放问题。
非常动态的内存分配会造成内存碎片,并可能耗尽系统的资源。
对于使用了动态内存分配的那些应用程序来说,增强健壮性的一种办法是用预分配缓冲区池(Preallocatedbufferpool)的办法来取代malloc()调用。
由于uclinux中不使用虚拟内存,进出内存的页面交换也没有实现,因为不能保证页面会被加载到RAM中的同样位置。
在普通计算机上,操作系统允许应用程序使用比物理内存(RAM)更大的内存空间,这往往是通过在硬盘上设立交换分区来实现的。
但是,在嵌入式系统中,通常都用FLASH存储器来代替硬盘,很难高效地实现内存页面交换的存取,因此,对运行的应用程序都限制其可分配空间不大于系统的RAM空间。
最后,uClinux目标板处理器缺乏内存管理的硬件单元,使得Linux的系统接口需要作些改变。
有可能最大的不同就是没有fork()和brk()系统调用。
调用fork()将复制出进程来创建一个子进程。
在Linux下,fork()是使用copy-on-write页面来实现的。
由于没有MMU,uclinux不能完整、可靠地复制一个进程,也没有对copy-on-write的存取。
为了弥补这一缺陷,uClinux实现了vfork(),当父进程调用vfork()来创建子进程时,两个进程共享它们的全部内存空间,包括堆栈。
子进程要么代替父进程执行(此时父进程已经sleep)直到子进程调用exitI()退出,要么调用exec()执行一个新的进程,这个时候将产生可执行文件的加载。
即使这个进程只是父进程的拷贝,这个过程也不能避免。
当子进程执行exit()或exec()后,子进程使用wakeup把父进程唤醒,父进程继续往下执行。
注意,多任务并没有受影响。
哪些旧式的、广泛使用fork()的网络后台程序(daemon)的确是需要修改的。
由于子进程运行在和父进程同样的地址空间内,在一些情况下,也需要修改两个进程的行为。
很多现代的程序依赖子进程来执行基本任务,使得即时在进程负载很重时,系统仍可以保持一种“可交互”的状态,这些程序可能需要实质上的修改来在uClinux下完成同样的任务。
如果一个关键的应用程序非常依赖这样的结构,那就不得不对它重新编写了。
假设有一个简单的网络后台程序(daemon),大量使用了fork()。
这个daemon总监听一个知名端口(或套接字)等待网络客户端来连接。
当客户端连接时,这个daemon给它一个新的连接信息(新的socket编号),并调用fork()。
子进程接下来就会和客户端在新的socket上进行连接,而父进程被释放,可以继续监听新的连接。
uClinux既没有自动生长的堆栈,也没有brk()函数,这样,用户空间的程序必须使用mmap()命令来分配内存。
为了方便,在uclinux的C语言库中所实现的malloc()实质上就是一个mmap()。
在编译时,可以指定程序的堆栈大小。
1.3通用架构的内核变化
在uClinux的发布中,/linux/mmnommu目录取代了/linux/mm目录。
前者就是修改后的内存管理子系统被修改,去除了MMU硬件的依赖,并在内核软件自身提供基本的内存管理函数。
很多子系统需要被重新修改、添加或者重写。
内核和用户内存分配和释放进程必须重新实现。
对透明交互/页面调度的支持也被去除。
内核中,加入了支持“位置无关代码(PIC)”的程序加载模块,并使用了新的二进制目标码格式,称为“扁平”格式(flat),用来支持PIC(有非常紧凑的头部)。
内核也提供了支持ELF格式的程序加载模块,用来支持使用固定基准地址的可执行程序。
两种模式各有利弊,传统的PIC运行快,代码紧凑,但是有代码大小限制。
例如Motorola68K架构的16位相对跳转限制了PIC程序不能超过32kbyte大小。
而采用运行期固定基址的方法使得没有了代码大小限制,不过当程序被内核加载后招致了较多的系统开销。
1.4uCLinux的内核加载方式
uCLinux的内核有两种可选的运行方式:
可以在flash上直接运行,也可以加载到RAM中运行。
Flash运行方式:
把内核的可执行映像文件烧到flash上,系统启动时从flash的某个地址开始逐句执行。
这种方法实际上是很多嵌入式系统采用的方法。
内核加载RAM方式:
把内核的压缩文件存放在flash上,系统启动时读取压缩文件在内存里解压,然后开始执行,这种方式相对复杂一些,但是运行速度可能更快。
同时这也是标准Linux系统采用的启动方式。
1.5uCLinux的文件系统
uCLinux系统采用ROMFS文件系统,这种文件系统相对于一般的ext2文件系统要求更少的空间。
空间的节约来自于两个方面:
首先内核支持ROMFS文件系统比支持ext2文件系统需要更少的代码;
其次ROMFS文件系统相对简单,在建立文件系统超级块(superblock)需要更少的存储空间。
ROMFS文件系统不支持动态擦写保存,对于系统需要动态保存的数据采用虚拟RAM盘的方法进行处理(RAM盘将采用ext2文件系统)。
应用程序如果需要以文件方式交换数据,可以将它存储在/tmp目录下。
这一目录实质上就是虚拟的RAM盘。
不过在掉电时,这些数据就会丢失。
如果希望在掉电时,信息仍然可以保持,那么就要把它写到FLASH中。
这时,就可以使用JFFS这一文件系统,在uClinux的发布中,文件“/linux/drivers/block/flash.c”中提供的JFFS代码可以参考。
另外,还需要修改/linux/.config和include/linux/autoconf.h中的有关内容,增加对FLASH和JFFS的编译。
2uClinux程序设计要点
2.1 软件开发工具
可以免费获得的GCC(GNU CCompiler)无疑是uClinux上最佳的开发工具。
uClinux系统的软件开发需要在标准Linux平台上用交叉编译工具来完成。
除了前面所提到的一些涉及内存和系统调用的程序之外,在x86版本的gcc编译器下编译通过的软件通常不需要做大的改动就可以用交叉编译工具编译到uClinux上运行。
交叉编译器可以从下面网址获得:
http:
//www.uclinux.org/pub/uClinux/m68k-elf-tools/
目前最新的版本是2.95.3:
m68k-elf-tools-20020410.tar.gz
交叉编译器直接解在根目录(/)下就行了:
(注意当前目录是/,而且m68k-elf-tools-20020218.tar.gz要在/下)
tarxzfm68k-elf-tools-20020218.tar.gz
它会自动在/usr/local/下建立起整套m68k的ELF交叉编译器,要编译自己的简单C程序就可以用/usr/local/bin/m68k-elf-gcc,例如,源代码为test.c,那么可以这样编译:
/usr/local/bin/m68k-elf-gcc –Wall-elf2flt -m5307 test.c–lc–otest.out
参数“-Wall”指定产生全部的警告;
-elf2flt指定自动调用elf转换flat格式的工具;
-m5307指定了处理器的指令集;
-lc指定了链接信息(ld);
-o指定输出文件的名字。
编译成功后得到的test.out就可以在uClinux环境上运行(通过nfsmount、smbmount,或者直接放到内核映像中都可以)。
也可以建立一个简单的Makefile来做这件事情:
CC=/usr/local/bin/m68k-elf-gcc
all:
$(CC)-Wall-elf2flt-m5307test.c-lc-o test.out
通过GDB可以调试目标板,Coldfire处理器可以通过Motorola的BDM作为调试接口,可以在不干扰程序正常运行的情况下调试目标板上的内核。
如果处理器不支持,那么在内核中需要插桩(stub),GDB和stub通过串行口或者以太网通讯。
2.2 可执行文件格式
先解释几种可执行文件格式。
coff(commonobjectfileformat):
一种通用的对象文件格式;
elf(excutivelinkedfile):
一种为Linux系统所采用的通用文件格式,支持动态连接和重定位;
flat:
扁平格式。
elf格式有很大的文件头,flat格式对文件头和一些段信息做了简化,可执行程序小。
uCLinux系统目前支持flat和elf两种可执行文件格式。
2.3uCLinux的应用程序库
uCLinux小型化的另一个做法是重写了应用程序库,相对于越来越大且越来越全的glibc库,uClibc对libc做了精简。
最新版本的程序库可以从这个网址获得:
http:
//uclibc.org/download/uClibc-0.9.10.tar.gz
uCLinux对用户程序采用静态链接的形式,这种做法会使应用程序变大,但是基于内存管理的问题,也就是基于没有MMU的特性,只能这样做,同时这种做法也更接近于通常嵌入式系统的做法。
uClibc提供大多数的类UNIX的C程序调用。
如果应用程序需要用到uClibc中没有提供的函数,这些函数可以加到uClibc中、或者作为一个独立的库、或者加到应用程序上面来进行链接。
2.4 裁减uCLinux以适合自己的应用
不同的嵌入式系统之间的根文件系统内容差异很大。
uClinux的发布包括一个根文件系统,实现了一个小型的类UNIX服务器,在串口上有控制台,telnetdaemon,web server,NFS客户端支持和一些可选的常用工具。
但是,有的系统设计可能不需要控制台,例如,如果设计一个可以播放MP3的随身CD机,内核只需要支持CD驱动、并行I/O和音频DAC。
而在用户空间可能只包括一个接口程序来驱动按钮、LED、来控制CD播放,并调用MP3的解码程序。
2.5关于uClinux 2.4内核
2001年发布了uClinux2.4,支持龙珠和coldfire处理器,基于2.4内核的一些增强特性同样做到了uclinux2.0系列上。
目前一些应用仍然基于2.0.x空间的。
然而2.4内核使得开发者可以访问很多新的特性,例如支持USB、IEEE火线、IrDA和新的网络特点,例如带宽分配、QoS、IPv6等等。
如果在应用程序中需要使用2.4的新特性,可以在编译uClinux内核时,选择2.4的版本。
不过,2.4毕竟是较新的内核,有可能不稳定的风险。
3 高效的程序开发
总的来说,在uClinux上的开发和标准Linux还是很类似的。
通常可以按照下面的步骤去设计和调试:
1.建立基于以太网的开发环境;
2.如果所设计的程序和硬件的关联不大,那么一定要在标准Linux上先编译和调试通过。
灵活地使用gcc和gdb将大大节省时间;
3.将x86上的GCC编译好的应用程序用交叉编译工具来编译;
如果编译时发现错误,那么很可能存在以下问题:
⏹交叉编译器或库文件的路径不正确;
最彻底的解决办法是重新装一次编译器;
⏹遇到库不支持的函数;
此时需要自己把函数的实现做成另外一个库供应用程序使用。
如果是uClinux本身不支持的调用,那么就需要改写代码了。
⏹C++的一些写法不太标准,需要修改;
4.通过网络(nfsmount)运行交叉编译成功的应用程序;
5.如果程序工作初步正常,那么就可以进一步在板子上测试了;
否则,需做修改重新编译,尤其要检查与uClinux的内存特性有关的代码。
如果程序较小,设计时就可以比较灵活。
如果是一个比较庞大的工程,那么,建立一个好的编译环境非常重要。
下面,给出一个较为复杂的工程中所建立的一系列编译文件例子,用好它们可以提高效率。
例如,在编译x86平台上的程序时,可以这样输入:
>
makeconfig
出现提示:
TargetPlatformSelection
Choose aVendor/Productcombination.
Vendor/Product[Intel/i386| Motorola/M5307C3]
如果我们输入i386,那么就可以编译出PC上运行的程序;
而输入M5307C3,就可以编译出uClinux上运行的程序。
这对于经常需要在标准Linux和uClinux之间进行交叉编译和查找错误的程序来说非常方便。
首先,建立图 2所示的整个工程目录结构。
图 2 整个工程的目录结构
在config中,存放一些编译有关的配置文件;
src目录下,按照各个程序模块分别创建一个目录。
在Project目录下,是整个工程的Makefile文件,如下:
###########################################################
#
#Makefile–WholeProjectmakefile.
#Copyright (c)2001-2002,Tsinghua MAC
#
###########################################################
# Getthecorestuff workedout
ROOT_DIR=$(shellpwd)
SRC_DIR = $(ROOT_DIR)/src
CONFIG_DIR =$(ROOT_DIR)/config
SCRIPTS_DIR=$(ROOT_DIR)/scripts
CONFIG_SHELL:
=$(shellif[-x"
$$BASH"
];
thenecho$$BASH;
\
elseif[ -x/bin/bash];
then echo /bin/bash;
elseecho sh;
fi;
fi)
TOPDIR:
=$(shellif ["$$PWD"!
= "
"
];
thenecho $$PWD;
elsepwd;
fi)
HPATHﻩ=$(TOPDIR)/include
FINDHPATHﻩ=$(HPATH)/asm$(HPATH)/linux$(HPATH)/scsi$(HPATH)/net
exportROOT_DIR SRC_DIR CONFIG_DIR SCRIPTS_DIRHPATHFINDHPATH
###########################################################
# normalmaketargets
.PHONY:
all
all:
$(MAKE)-C $(SRC_DIR)all
###########################################################
#Configstuff, werecallourselvestoload thenewconfig.archbefore
# running thekernelandother configscripts
.PHONY:
config
config:
$(CONFIG_DIR)/config.in
cd$(CONFIG_DIR);
$(CONFIG_SHELL)$(SCRIPTS_DIR)/Configure $(CONFIG_DIR)/config.in
ﻩ@rm-f $(SRC_DIR)/arch.config
ﻩ@ifegrep"
^CONFIG_DEFAULTS_INTEL_I386"
$(CONFIG_DIR)/.config >
/dev/null;
then\
ﻩln-s"
$(CONFIG_DIR)/arch.i386" $(SRC_DIR)/arch.config;
\
fi
@if egrep"
^CONFIG_DEFAULTS_MOTOROLA_M5272C3"
$(CONFIG_DIR)/.config>
/dev/null;
then\
ln -s"
$(CONFIG_DIR)/arch.m68k"
$(SRC_DIR)/arch.config;
\
ﻩ fi
@echo"
#Thisdir.configfileisautomaticlygeneratedbymakeconfig!
>
$(SRC_DIR)/dir.config
@echo"
ROOT_DIR="
$(ROOT_DIR)>
$(SRC_DIR)/dir.config
@echo"CONFIG_DIR="
$(CONFIG_DIR)>
$(SRC_DIR)/dir.config
@echo "
SRC_DIR="
$(SRC_DIR)>
$(SRC_DIR)/dir.config
@echo"
SCRIPTS_DIR="
$(SCRIPTS_DIR)>
> $(SRC_DIR)/dir.config
@echo"HPATH="
$(HPATH)>>
$(SRC_DIR)/dir.config
@echo"FINDPATH="$(FINDPATH)>
$(SRC_DIR)/dir.config
###########################################################
#normal makedependancy
.PHONY:
dep
dep:
$(MAKE)-C$(SRC_DIR)dep
#Thisone removesallexecutables from thetree andforcestheir relinking
clean:
$(MAKE) -C$(SRC_DIR)clean
test:
ﻩ$(MAKE) -C$(SRC_DIR)test
run:
ﻩ$(MAKE) -C $(SRC_DIR) run
config_error:
ﻩ@echo"*************************************************"
ﻩ@echo"
Youhave notrunmakeconfig."
Thebuildsequenceforthis sourcetreeis:
"
ﻩ@echo"
1.'
makeconfig'
or'
makexconfig'
ﻩ@echo "2.'
make dep'
ﻩ@echo"
3. '
make'
ﻩ@echo"
*************************************************"
ﻩ@exit 1
###########################################################
src目录下的Makefile文件,如下: