嵌入式Linux系统软件开发

 
嵌入式Linux系统软件开发
1. 开发平台的选择:
 
一个项目拿到手,如何选择开发平台(主要是指CPU和操作系统以及开发环境和工具)应该说至关重要,有时这不光影响进度,产品质量,可维护性等一般问题,甚至涉及到方案的可实现性。
从系统功能实现考虑:
(1)            是否有片上外设,专用指令或配套的软件模块直接实现系统功能要求。感觉这一条对很多人的决策影响很大。
(2)            价格,这一点应通过CPU提供的资源综合考虑,它提供了多少有用的资源,多少没用的资源,还是那三个字,性价比,另一方面,是要抓主要矛盾,是不是有些特性是必须的。
(3)            功耗,本系统对CPU功耗要求不高,但对移动设备,这一点可是致命,而且这一点不是仅针对CPU,所有器件都要勒紧裤腰带运行。
(4)            处理速度,这项不用多说,大家都明白重要性,但具体算起来可是一门学问,一方面是自己需要多少,如果加上一般操作系统这事都不好控制,余量还是大点稳妥,另一方面,CPU操作指令周期多少,有没有流水,有没有并行,什么体系结构,有没有专用指令,对外部存储器和外设的存取速度等等,哪一个慢了都叫瓶颈。
(5)            需要的硬件支持(如外部存储器,双电源等),这算是杂项,单会增加额外的价格,系统体积等,不容忽视。
从开发者的角度考虑:
(1)            是否有足够的技术支持包括demo版及原理图,demo程序,操作系统和BSP,测试开发工具。
(2)            自身条件,包括对项目开发周期的要求,使用人员对器件和开发模式的熟悉程度以及掌握的难易程度。
(3)            可用资源是否丰富(书籍,网络等)
(4)            系统的可继承性,可移植性和可扩展性。
(5)            方案提供商的素质。(包括技术水平和服务意识)。
 
2. 开发过程:
 
嵌入式Linux系统下的应用开发最重要的一步就是“交叉编译”。
编译的最主要的工作就在将你的程序转化成运行该程序的CPU所能识别的机器代码,不同的CPU有相应的编译器,另一方面。编译器本身也是程序,当然也要在某一个CPU平台上运行。于是交叉编译的交叉点就在那个编译器本身是CPU1上的一个程序,却在为CPU2编译代码。假如在你的ARM系统上,操作系统已经正常运行,并且你的资源足够多,你可以把PC机上运行的ARM编译工具移植到ARM上,然后所有该系统的应用程序都直接在ARM系统上编译,这就不算交叉编译,但如果有条件这么作,程序的开发或者移植就方便多了,因为整个开发过程又回到在自己PC机上编应用程序的那种模式了,那就是在自己的地盘上用自己的编译器编自己的应用程序。
       与不使用操作系统的开发模式不同(此处的操作系统尤其指提供了专门的接口函数库的操作系统),在目标板(就是实现系统的板子)使用操作系统的开发模式下,交叉编译环境中还需要该对应该操作系统的库。比如uClinux提供的uClibc。此时,开发用的主机上不光要有目标板CPU所需的编译工具,还要有对应操作系统的库,又因为一般库文件还要在开发机上拿目标CPU的编译器重新编译一下,所以还要把操作系统的原码也放到开发机上。
       虽然操作系统的接口库至关重要,但大家似乎已经淡忘了它的存在。这些多是因为大家已经远离了刀耕火种的年代(需要告诉编译器需要的include路径,lib路径,以及lib的名称),集成的编译环境让我们编译链接的所有繁琐工作化作对BUILD按钮的潇洒一击。而且不论是windows环境,还是linux环境,都有环境变量去记录这些参数。但尝试将/usr/lib目录改一个名字,你就会知道你不能无视他们的存在,因为操作系统的功能都是通过这些库来交给应用层程序使用的。
       以上的东西一般时候是没有必要仔细研究,但交叉环境下开发或移植比较大的程序时,你可能就需要了解编译器,链接器等开发工具的几乎所有重要参数。
       在我的系统上,建立基本的开发环境过程如下。
(1)            安装gnu开发工具链(是GNU开发的针对ARM CPU的一组编译开发程序(是linux程序)。包括arm-elf-gcc,arm-elf-ld等。
(2)            将uClinux源代码源代码解压到相应路径下,按照编译内核的步鄹编译一遍(此时使用的编译工具已经是上面提到的ARM编译工具了,因为它要在ARM CPU上运行,另外,和编译linux内核一样,此时可以通过menuconfig来对内核提供的功能进行裁减。
(3)            将库(uClibc)解压到相应路径下,用以上工具编译一遍。
这样最基本的环境就算搭建好了。
 
3. 一般程序的开发:
 
因为目标板上用uClinux,它提供的程序接口和linux下的基本一致,基于这种开发模式的应用程序开发变成了linux下的程序开发。而且在实际中一般是编好了程序先在主机上拿主机平台上的编译器编译并且调试一下(linux下的编译器就是gcc了),当然前提是被调试的程序中需要的硬件条件主机具备,调通以后拿目标板的编译器重新编译一下,下载到目标板上运行,一般来说就可以直接用了。
以上也是为什么我认为开发嵌入式linux程序主机应该选用linux环境。对于以前没用过linux的人来说,开发程序前应该花3,4天时间熟悉linux环境,尤其是它的编辑器,用惯集成编译环境的人有时连编译器和编辑器的概念都模糊了,所以一般是直接进入集成编译环境,连写带编一气呵成,殊不知有些集成编译器提供的编辑器弱智的一塌胡涂,如果用熟了linux下的emacs或vi,虽然刚使用它的感觉很有可能是复杂,但只要多用多练,对提高效率很有帮助。
现在重点想说些关于编译器的东西,不了解它,在交叉编译环境下编译程序就寸步难行了。想想在linux下将myprogram.c编译链接成应用程序myprogram,最简单的一句gcc –o myprogram  myprogram.c就可以了。(其实在诸如VC下你也可以找到类似的命令,集成开发环境只不过替你来调用它了)。一切看起来天经地义。
但试着把/usr/include路径改一个名字(比如改成stupid_include),再这样编译一下,会发现程序中被<  >引用的头文件(比如#include<stdio.h>)都找不到了。因为编译器看见这样的头文件会到系统指定的路径下寻找,而这个路径是由环境变量保存的(linux和windows下都是这样的)。针对以上情况,不将路径名字改回去,但是给编译器加一个参数如下:gcc –I/usr/stupid_include –o myprogram myprogram.c 会发现错误信息没了,一切又恢复了往日的宁静,顿时明白,不用环境变量,通过参数,同样可以将这些信息告诉编译器。   返回来说说你的目标编译器,虽然占用了编译器,头文件,库文件,一个都不少,但你要编一个程序编译器照样发晕,因为没有环境变量告诉它自己需要的头文件和库文件在哪里。看来只有两种办法,一个是抢占了主机的环境变量改成自己的,或者在编译时加上必要参数,告诉编译器需要的文件的位置。(除此之外,还有其他一些参数也是如此)。
从源程序到可执行文件根据情况不同可能分好几步,一般每一步可能都会有一个应用程序实现,像gnu提供的arm开发工具链其实就是这么一组程序。提供从编译到链接到格式转化的全套服务。你可以用arm-elf-gcc命令一步到底直接产生可执行文件,也可以每一步加上自己的参数,只作自己的事。
编译器的主要参数的使用下次将程序的移植时再讲。这里想说一下编译器产生应用程序的几个主要步骤,讲这个问题的原因还是很多人无法区分诸如编译和链接,不用问,这一切还是IDE集成开发环境惹的祸。首先以上说的东西一般在IDE的project菜单下的option或build option中找到,只是一般不用管罢了。另一个方面,IDE就像是傻瓜相机,很多工作他都帮你完成了,使用简单。但如果要做摄影师的话,你就少不了要对每一个细节都了解。其实编译程序也是一样。(你可以对优化,警告级,宏定义等诸多选项进行自己的选择)。以下是几个主要步骤:
(1)            预编译。主要工作就是处理所有#开头的,包括头文件。以前搞不清头文件和可执行文件有没有什么联系(因为总看见两个文件名字取一样的),现在知道,他们之间没有任何联系。在预编译结束后,头文件的使命就结束了。在下一次介绍不同平台程序移植时可以看到,预编译有时非常有用。
(2)            编译。编译应该是最主要的一步,就是将源文件生成CPU能识别的语言,一般是后缀为.o的目标文件,应该说,此时的文件就已经可以执行了。当然这个时候外部函数等外部符号都没有引入,对于被编译程序来说,这些外部符号压根儿不知它在不在。你可以在你的程序里调用一个不存在的函数,甚至都不用声明,在编译阶段,很多编译器只是给个警告。只有在链接时才会报错。
(3)            链接。以前在程序里用到的外部符号都要把真正的东西交出来。你可以指定需要连接在一起的目标文件,也可以告诉编译器库文件的名字和路径。编译器会去找,需要注意的是,库的指定需要注意顺序。首先,如果不同的库里有同名函数,并且该函数被调用,那么在前面的就被链接进去了,这一点对于头文件路径的指定也适用,如果你自己的头文件和系统头文件相同,并且你的头文件路径在系统头文件路径前面,你的头文件就会代替头文件。库文件是由相应的程序将需要被添加到库里的目标文件(该文件是编译阶段生成的)组织起来生成档案文件,同时可以建立一个检索,检索内容为所包含的目标文件中定义的符号。也就是说,库文件并不是必须的,但它为经常使用的目标文件中的函数提供了快速的检索机制。
程序开发过程中调试也是至关重要,因为可以先在主机上调试,所以可以使用linux下的gdb,(有点像dos 下的debug)。
 
4. 程序的移植: Linux
 
首先列出一些问题:
X86上运行的程序能不能在ARM机上运行,为什么,有没有可能,如果可以,应该做哪些工作才可以实现。
相同CPU平台,DOS的程序为什么可以在windows下运行,能不能在linux下运行,为什么,作什么工作有可能该程序运行。
为什么可以移植程序,为什么要移植程序?
 
程序可以移植首先要感谢开发出高级语言的天才,记住,无论多么漂亮的代码经过编译以后都要变成CPU可以识别的机器语言,而几乎一千种CPU说着一千种语言。为保证大家有共同语言,规定一种高级语言。每一个CPU派出自己的翻译――编译器。这个翻译精通两国语言,高级语言和自己的语言。(由此已经可以看出编译工具在程序移植中的重要性)。只要程序没有硬件上的约束,可以说这种沟通是无极限的,甚至于不同操作系统平台。(操作系统也是程序,也可以移植喽)举例:在x86的windows下用VC(或TC,BC)编一个c程序实现printf(“hello, world!/n”),丝毫不改就可以用ARM的C编译器重新编译并在ARM机上运行。一次小型的移植就结束了。
那么为什么要移植程序?
当然是为了在不同的平台上使用程序。
什么时候可以考虑移植程序?
程序的移植最终只针对CPU,其实和操作系统没什么关系,但另一方面,因为代码中可能会使用一些库函数,这些库会包括C语言标准库和操作系统提供的API(应用程序接口)库。假设源代码中只包括C标准库,那么该程序就可以跨操作系统去移植。例如hello world程序中使用了printf,因为该函数是C标准函数,所以在X86上使用TC(BC或VC)可以直接编译运行,在ARM+uClinux平台下也一样,但如果程序中调用了vfork函数,那么只有linux一脉相承的操作系统支持这种特殊服务了,在window或dos操作系统下无法直接编译该程序了。只能找该操作系统支持的类似的功能来实现。
如果是在各种嵌入式linux(除了uClinux以外,还有好几种)平台上开发,那么针对该平台以及linux平台上的源代码都可以使用,但是要牢记他们之间的差异。
移植应用程序的前提是有源代码,移植的关键工具是编译器,源代码中和硬件平台相关的东西越少越好(这里主要指使用了汇编,或做了针对自己平台的事,比如将指针指向特定地址然后操作),另一方面,如果该程序是基于某个操作系统(利用了操作系统提供的特殊服务,即API),要看自己的操作系统是否提供了相关服务。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值