您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > 04-Linux应用程序_part1
嵌入式Linux应用程序开发4.嵌入式Linux应用程序开发简介嵌入式Linux应用程序应用程序开发不同于PC上开发应用程序,最典型的差异叫做交叉编译(CrossCompilation),也就是在PC的x86平台上开发在ARM处理器上运行的程序。交叉编译的不是一个新鲜的概念,其实单片机开发的编译环境就是典型的一种人交叉编译环境,在PC上开发的程序,在单片机上运行。但是针对嵌入式Linux开发程序的时候,需要在PC的Linux环境和嵌入式Linux环境中切换,两个环境越来越接近,对于初学者来说容易混淆。嵌入式系统的硬件处理能力越来越强大,是否可以在嵌入式系统上像PC上开发程序一样实现本地编译呢?答案是可以!但一般认为PC的处理能力更加强大,资源更丰富,几乎所有开发工具都是在x86平台上开发,稳定性方面都比在ARM平台上的开发工具要稳定。此外,在x86上工作的交叉编译开发人员远大于在ARM平台上进行本地编译开发的人员要庞大得多,bug解决得更好、更快。所以,目前建议采用交叉编译方式开发嵌入式Linux应用程序。当然,也可以尝试在嵌入式Linux系统上安装GCC,享受一下本地编译开发的便捷。本章的内容主要分为三个部分:第一部分从4.1-4.x,将通过数个Linux应用程序的示例来展示在命令行模式下程序开发、调试、部署的全部过程。读者将比较全面的了解Linux应用程序开发的各项内容:进程、线程、网络、系统调用、硬件访问示例的内容......。第二部分,展示如何在集成开发环境中进行应用程序开发、调试、部署的全过程。第三部分,示例访问系统中的各种传感器,对传感器进行配置、采集传感器的数据。4.1HelloWorld命令行开发世界上最经典的计算机程序,每个初学者都倒背如流的demo。$./HelloWorldHelloWorld!无需过多解释程序的结构、内容,这里要说的是这个demo的意义:1.测试交叉编译环境是否安装正确;2.了解gcc编译器使用方法,以及基本参数;3.如何执行交叉编译的可执行程序。4.1.1开发步骤1.为项目创建开发目录,将项目相关的所有文件都集中在该目录下mkdirHelloWorld_CMDcdHelloWorld_CMD2.创建main.c源文件,命令行界面下可以使用vi编辑器,图形界面下可以使用gedit。3.在文本编辑器中输入下面代码:#includestdio.hintmain(intargc,char*argv[]){printf(HelloWorld!\n);return0;}4.保存main.c源文件5.交叉编译main.c为ARM指令的可执行程序:C语言编译编译器是arm-linux-gnueabihf-gcc,编译单个源文件main.c:$arm-linux-gnueabihf-gccmain.c生成a.out可执行程序。4.1.2扩展1.缺省的文件名不能望文生义,如何自定义可执行程序名称?至少有两种办法:A.使用文件操作命令将a.out修改为自定义的名称;下面的命令将可执行程序命名为HelloWorld:$mva.outHelloWorldB.使用gcc的-o参数;下面的命令将可执行程序命名为HelloWorld:$arm-linux-gnueabihf-gccmain.c-oHelloWorld4.1.2运行交叉编译的可执行程序Windows中,可执行程序通常用.exe作为后缀,在Linux中的惯例是没有后缀。生成的可执行程序不能在x86机器上直接运行,有很多种方法让它执行:1.在ARM处理器上执行;2.在ARM处理器模拟程序中执行。由于我们已经有了开发板,本示例中介绍在ARM处理器上执行交叉编译好的可执行程序的方法:A.将可执行程序复制到SD卡的Linux分区,建议复制到/home/root/demo目录,没有请自行创建(如果用户熟悉Linux,当然也可以在其它地方);B.在PC虚拟机的x86UbuntuLinux中,建立NFS文件共享服务器,将可执行程序复制到共享目录中,然后,使用嵌入式Linux访问NFS服务器,运行可执行程序。注意:由于可执行程序所在的路径并非shell搜索可执行命令的缺省路径中,所以在shell中运行可执行程序需要指定路径:$/home/root/demo/HelloWorld如果恰好当前路径就是/home/root/demo,则可以使用下面的方式:$./HelloWorld“./”代表的是当前路径,“../”代表的是上一级路径。4.2多文件软件项目本示例由main.c、reciprocal.c、reciprocal.h等几个文件组成,用来计算在程序启动时用户由命令行传递给程序的一个整数的倒数,并以字符串的形式输出计算结果。$./reciprocal10Thereciprocalof10is0.1示例的目标:1.了解程序如何从命令行获取传递给它的参数;2.使用assert检查发现bug;3.了解由多文件构成的程序如何生成可执行程序的过程;4.学习更多的gcc参数使用。4.2.1开发步骤1.创建main.c源文件,并将下列内容编辑到文件中保存:#includestdio.h#includestdlib.h#includereciprocal.hintmain(intargc,char**argv){inti;i=atoi(argv[1]);printf(Thereciprocalof%dis%g\n,i,reciprocal(i));return0;}2.创建reciprocal.c源文件,并将下列内容编辑到文件中保存:#includeassert.h#includereciprocal.hdoublereciprocal(inti){//Ishouldbenon-zero.assert(i!=0);return1.0/i;}3.创建reciprocal.h源文件,并将下列内容编辑到文件中保存:#ifndef_RECIPROAL_H_#define_RECIPROAL_H_externdoublereciprocal(inti);#endif4.执行下面命令,生成可执行程序:$arm-linux-gnueabihf-gcc-cmain.c$arm-linux-gnueabihf-gcc-creciprocal.c$arm-linux-gnueabihf-gcc-oreciprocalmain.oreciprocal.o4.2.2解析程序的结构很简单,这里解析几个可能的问题。1.如何获取用户在命令行输入的参数?请注意main.c的intmain(intargc,char**argv)函数有两个参数,用户在shell中启动程序时所带的参数(命令行参数,command-linearguments,参数之间通过空格分隔),通过argc、argv两个参数可以访问命令参数列表的内容。argc表示参数的数量,argv是字符指针数组,每一个指针指向一个参数,都是NULL结尾的字符串。参数列表的第一个总是程序名。代码中使用的“argv[1]”就是紧跟在程序名之后的第二个参数。2.使用assert宏辅助代码调试reciprocal.c是求倒数的源文件,0的倒数是非法的,所以不应该计算0的倒数。源代码中使用了assert(i!=0),当i!=0时,相安无事,若i=0时,将程序将出现下面错误信息:$./reciprocal0reciprocal:reciprocal.c:30:reciprocal:Assertion`i!=0'failed.Aborted(coredumped)信息里提供了:出错的文件名、出错行号等信息,是一个检查程序错误非常好的debug工具。assert宏的参数是一个bool量,可以是bool表达式。如果bool量或者表达式为false,程序将终止,然后打印出相关的错误消息。assert常常被用于检查函数参数、返回值检查等等。assert不仅仅是运行期检查,也可以看成是程序文档。包含assert的代码,告诉使用者某些条件必须为true,如果为false则说明程序有bug。但是,assert宏不是一种用来处理程序运行期错误的机制。首先,程序错误与运行期错误是两码事。程序错误是一个bug,不应该出现;运行期错误可能在程序执行的任何时候出现。例如,用户输入了一个0参数,就是典型的运行期错误,用assert处理就不是一个好的方法。不应该让程序异常中断,输出用户不友好的提示信息。assert经常用于空指针的检查,避免函数调用时传递空指针。也可以对运算条件、运算结果进行检查,帮助发现运算过程中的动态错误。Assert也可以用于可重用的代码库(library,动态库、静态库)中,可以让用户代码没有bug地调用该代码库。因为库不知道自己使用的环境,库函数本身不进行错误处理,使用assert让调用者对错误情况进行检查,防止错误调用。对于性能要求很高的代码,大量使用assert的运行期检查会对性能有很大冲击,可以使用NDEBUG宏定义,在编译预处理阶段将assert代码忽略(请参考4.2.3)。注意:不要在assert中调用函数,如下代码不好:for(i=0;i100;++i)assert(do_something()==0);建议修改为下面的样子:for(i=0;i100;++i){intstatus=do_something();assert(status==0);}3.reciprocal.h中的#xxxxxx定义有什么用?为每一个头文件定义一个唯一的宏(这里是_RECIPROAL_H_,一般是跟头文件的文件名差不多的大写字符串),防止头文件之间相互引用时出现头文件重复引用的错误。每个C/C++头文件都必须包含和reciprocal.h类似的结构,这是一个良好的编程习惯。4.生成可执行程序的过程。第一步将所有源代码(*.c)生成目标代码(*.o);第二步将所有目标代码链接成可执行程序。gcc的-c参数是将源代码编译为目标文件(objectfile)。如果没有参数gcc将尝试对该文件进行链接生成可执行程序。4.2.3扩展1.GCC头文件搜索参数-I参数告诉gcc在哪些路径搜索头文件。缺省情况下,gcc搜索当前工作路径,以及标准库安装路径下的头文件。由于reciprocal.h头文件位于当前工作路径,因此,无需用-I参数指定。2.GCC宏定义参数在命令行中定义宏。例如在软件的发行版本中,不需要过多的assert检查,可以通过定义NDEBUG宏来关闭这些assert检查。最好的做法是:$gcc-c-DNDEBUGxxx.c也可以定义NDEBUG的级别$gcc-c-DNDEBUG=3xxx.c编译器级别的代码优化,软件发行版本通常会采用,让程序运行更快。常见的优化参数-O2(Optimizationlevel2)适合大多数程序。$gcc-c-O2xxx.cpp注意:使用优化参数后调试程序会更困难,也可能发现一些之间不明显的bug。C++编译器是g++,操作类似gcc。4.2.4思考1.用户在启动程序时,如果没有输入任何参数时程序会出现“段错误”,请修正该错误,提示用户没有输入参数。2.Assert宏在这里不是很好的运行期错误处理机制,请修改代码,对用户输入参数进行检查,提供更好的人机交互。3.让程序支持小数输入。4.写一个程序,显示该程序启动时用户输入的所有参数。#includestdio.hintmain(intargc,char*argv[]){printf(Thenameofthisprogramis`%s'.\n,argv[0]);printf(Thisprogramwasinvokedwith%darguments.\n,argc-1);/*Wereanycommand-lineargumentsspecified?*/if(argc1){/*Yes,printthem.*/inti;printf(Theargumentsare:\n);fo
本文标题:04-Linux应用程序_part1
链接地址:https://www.777doc.com/doc-5528209 .html