《第9章基于ARM9和Linux嵌入式系统设计》由会员分享,可在线阅读,更多相关《第9章基于ARM9和Linux嵌入式系统设计(136页珍藏版)》请在金锄头文库上搜索。
1、第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.1嵌入式嵌入式Linux的开发环境的开发环境9.2Linux开发工具的使用开发工具的使用9.3GNUmake命令和命令和makefile文件文件9.4嵌入式嵌入式Linux引导程序引导程序9.5嵌入式嵌入式Linux下程序调试应用举例下程序调试应用举例第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.1 嵌入式嵌入式Linux的开发环境的开发环境9.1.1嵌
2、入式嵌入式Linux开发环境建立开发环境建立嵌入式嵌入式Linux Linux 开发环境有几个方案:开发环境有几个方案:(1 1)在)在WINDOWS下安装下安装Linux虚拟机后,虚拟机后,目前大多情况下使用目前大多情况下使用VWare软件;软件;(2 2)直接安装)直接安装 Linux操作系统。操作系统。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.1.2 9.1.2 嵌入式嵌入式LinuxLinux开发的一般过程开发的一般过程1.了解硬件了解硬件;2.准备需要使用的准备需要使用的Linux工具以及其他工具工具以及其他工具;3.安排内存地
3、址安排内存地址;4.编写启动代码和机器相关代码编写启动代码和机器相关代码;5.编写驱动程序编写驱动程序; ;6 6.C库、库、GUI和系统程序的移植;和系统程序的移植;7 7.调试调试 . .第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.2 Linux9.2 Linux开发工具的使用开发工具的使用 9.2.1 Linux9.2.1 Linux开发工具开发工具GNU GNU gccgcc的使用的使用1.GCC简介简介GCC是是GNUCompilerCollection的简称,的简称,GCC是是Linux平台下最常用的编译程序,是平台下最常用的编
4、译程序,是Linux平平台编译器的事实标准。台编译器的事实标准。GCC支持的体系结构有支持的体系结构有40余种,常见的有余种,常见的有x86系列、系列、Arm、PowerPC等。同时,等。同时,GCC还能运行在还能运行在不同的操作系统上,如不同的操作系统上,如Linux、Solaris、Windows等。等。GCC除了支持除了支持C语言外,还支持多种其他语言,语言外,还支持多种其他语言,例如例如C+、Ada、Java、Objective-C、Fortram、Pascal等。等。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2. GCC2. GCC
5、常用模式及选项常用模式及选项gccgcc最基本的用法是:最基本的用法是:gccgcc options file. options file. 其中其中optionoption是以是以“-”-”开始的各种选项,开始的各种选项,filefile是相是相关的文件名。在使用关的文件名。在使用gccgcc的时,必须给出必要的选项的时,必须给出必要的选项和文件名。和文件名。gccgcc的整个编译过程分别是:预处理、编的整个编译过程分别是:预处理、编译,汇编和链接。译,汇编和链接。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第第9 9章章 基于基于ARM9A
6、RM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 例如,例如,$ $ gccgcc -o hello -o hello hello.chello.c gccgcc编译器就会生成一个编译器就会生成一个hellohello的可执行文件。在的可执行文件。在hello.chello.c的当前目录下执行的当前目录下执行./hello./hello。gccgcc编译器生成的目标文件默认格式为编译器生成的目标文件默认格式为elfelf(executive linked fileexecutive linked file)格式,是)格式,是LinuxLinux系统所系统所采用的可执行链接文件的通用文
7、件格式。采用的可执行链接文件的通用文件格式。elfelf格式由若格式由若干个段(干个段(sectionsection)组成,由标准)组成,由标准c c源代码生成的目标源代码生成的目标文件中包含以下段:文件中包含以下段: .text.text(正文段)包含程序的指令代码。(正文段)包含程序的指令代码。 .data.data(数据段)包含固定的数据,如常量,字符串等。(数据段)包含固定的数据,如常量,字符串等。 . .bssbss(未初始化数据段)包含未初始化的变量和数组等。(未初始化数据段)包含未初始化的变量和数组等。GCCGCC常用两种模式:编译模式和编译连接模式。常用两种模式:编译模式和编译
8、连接模式。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 例:假设全部的源代码都在一个文件例:假设全部的源代码都在一个文件test.ctest.c中。中。$ $ gccgcc -o test -o test 此命令是把源文件此命令是把源文件test.ctest.c直接编译成可执行程序直接编译成可执行程序testtest。$ $ gccgcc -c -c test.ctest.c 此命令是把源文件此命令是把源文件test.ctest.c编译成不可执行目标文件编译成不可执行目标文件test.otest.o。默认情况下,生成的目标文件名为默认情况下,生
9、成的目标文件名为test.otest.o,但也可以为输出,但也可以为输出文件指定名称,如下所示:文件指定名称,如下所示:$ $ gccgcc -c -c test.ctest.c o o mytest.omytest.o 此命令是把源文件此命令是把源文件test.ctest.c编译成不可执行目标文件编译成不可执行目标文件mymytest.otest.o。 下面的命令将同时编译下面的命令将同时编译3 3个源文件,即个源文件,即first.cfirst.c、second.csecond.c和和 third.cthird.c,然后将它们连接成一个可执行程序,然后将它们连接成一个可执行程序testte
10、st。命令如下:。命令如下:$ $ gccgcc -o test -o test first.cfirst.c second.csecond.c third.cthird.c第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3 3其他常用选项的使用其他常用选项的使用$gcctest.cI./inc-otest此命令告诉此命令告诉GCCGCC包含文件存放在包含文件存放在./inc ./inc 目录下,在当前目录目录下,在当前目录的上一级。如果在编译时需要的包含文件存放在多个目录下,的上一级。如果在编译时需要的包含文件存放在多个目录下,可使用多个可使用多
11、个-I -I 来指定各个目录。如:来指定各个目录。如:$gcctest.cI./incI././inc2-otest此命令指出了另一个包含子目录此命令指出了另一个包含子目录inc2inc2,较之前目录,较之前目录它还要在再上两级才能找到。另外,还可在编译命令它还要在再上两级才能找到。另外,还可在编译命令行中定义符号常量。可简单的在命令行中使用行中定义符号常量。可简单的在命令行中使用-D-D选项选项即可,如下例所示:即可,如下例所示:$gccDTEST_CONFIGURATIONtest.c-otest上面的命令与在源文件中加入下列命令是等效的:上面的命令与在源文件中加入下列命令是等效的:#de
12、fineTEST_CONFIGURATION第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3其他常用选项的使用其他常用选项的使用$gcctest.cI./inc-otest此命令告诉此命令告诉GCC包含文件存放在包含文件存放在./inc目录下,在当前目录的上一级。若在目录下,在当前目录的上一级。若在编译时需要的包含文件存放在多个目录下,编译时需要的包含文件存放在多个目录下,可使用多个可使用多个-I来指定各个目录:来指定各个目录:$gcctest.cI./incI././inc2-otest 上上面面命命令令告告诉诉GCC包包含含文文件件存存放放在
13、在./inc目目录录下下,在在当当前前目目录录的的上上一一级级。若若在编译时需的包含文件存放在多个目录下,可使用多个在编译时需的包含文件存放在多个目录下,可使用多个-I来指定各个目录:来指定各个目录:$gcctest.cI./incI././inc2-otest 这里指出了另一个包含子目录这里指出了另一个包含子目录inc2,较之前目录它还要在再上两级才能找到较之前目录它还要在再上两级才能找到. 另另外外,我我们们还还可可以以在在编编译译命命令令行行中中定定义义符符号号常常量量。为为此此,我我们们可可以以简简单单的的 在命令行中使用在命令行中使用-D选项即可,如下例所示:选项即可,如下例所示:$
14、gcc-DTEST_CONFIGURATIONtest.c-otest 上面的命令与在源文件中加入下列命令是等效的:上面的命令与在源文件中加入下列命令是等效的:#defineTEST_CONFIGURATION第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 4. 4. 警告功能警告功能 当当GCCGCC在在编编译译过过程程中中检检查查出出错错误误,则则中中止止编编译译;但但检检测测到到警告时却能继续编译生成可执行程序。警告时却能继续编译生成可执行程序。 在在众众多多的的警警告告选选项项之之中中,最最常常用用的的是是-Wall-Wall选选项项。该该
15、选选项项能能发发现程序中一系列的常见错误警告,举例如下:现程序中一系列的常见错误警告,举例如下:$ $ gccgcc -Wall test.c -o test -Wall test.c -o test 该选项相当于同时使用了下列所有的选项:该选项相当于同时使用了下列所有的选项: unused-functionunused-function:遇到仅声明过但尚未定义的静态函数遇到仅声明过但尚未定义的静态函数时发出警告。时发出警告。 unused-labelunused-label:遇到声明过但不使用的标号的警告。遇到声明过但不使用的标号的警告。 unused-parameterunused-par
16、ameter:从未用过的函数参数的警告。从未用过的函数参数的警告。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.2.2 GDB9.2.2 GDB调试器简介调试器简介 LinuxLinux系统中包含了系统中包含了GNU GNU 调试程序调试程序gdbgdb,用来调试,用来调试C C和和 C+ C+ 程序的调试器。程序的调试器。gdbgdb 提供如下功能:提供如下功能: 运行程序,设置所有的能影响程序运行的参数运行程序,设置所有的能影响程序运行的参数和环境。和环境。 控制程序在指定的条件下停止运行。控制程序在指定的条件下停止运行。 当程序停止时
17、,可以检查程序的状态。当程序停止时,可以检查程序的状态。 修改程序的错误,并重新运行程序。修改程序的错误,并重新运行程序。 动态监视程序中变量的值。动态监视程序中变量的值。 可以单步执行代码,观察程序的运行状态。可以单步执行代码,观察程序的运行状态。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 1 1gdbgdb的启动的启动在终端窗口中,有两种方法运行在终端窗口中,有两种方法运行gdbgdb,即在终端窗口的命令行,即在终端窗口的命令行中直接输入中直接输入gdbgdb命令或命令或gdbgdb filename filename命令运行命令运行gd
18、bgdb。方法方法1:先启动先启动gdb后执行后执行filefilename命令。即命令。即gdbfilefilename执行上述两条命令就可启动执行上述两条命令就可启动gdb,并装入可执行的程序,并装入可执行的程序filename。方法方法2:启动启动gdb的同时装入可执行的程序。即的同时装入可执行的程序。即gdbfilename其中,其中,filename是要调试的可执行文件。这和启动是要调试的可执行文件。这和启动gdb后执行后执行filefilename命令效果完全一样。命令效果完全一样。启动启动gdb后,就可以使用后,就可以使用gdb的命令调试程序。的命令调试程序。第第9 9章章 基于
19、基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2gdbgdb的基本命令的基本命令gdb中的命令分为以下几类:中的命令分为以下几类:工作环境相关命令、工作环境相关命令、设置断点与恢复命令、源代码查看命令、查看运行数设置断点与恢复命令、源代码查看命令、查看运行数据相关命令及修改运行参数命令。据相关命令及修改运行参数命令。gdbgdb的命令可以通过的命令可以通过helphelp命令进行查找命令所属的种类(命令进行查找命令所属的种类(classclass),可以从相),可以从相关关classclass找到相应命令。如下所示:找到相应命令。如下所示:(gdb)help此命
20、令可列出命令的种类。此命令可列出命令的种类。(gdb)helpdata此命令查找此命令查找data类种的命令,并类种的命令,并列出列出data类种的所有命令。类种的所有命令。(gdb)helpcall此命令查找此命令查找call命令。命令。直接键入直接键入“help commandhelp command”来查看命令。来查看命令。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (1 1)工作环境相关命令)工作环境相关命令第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2) (2) 设置断点与恢复命令设
21、置断点与恢复命令第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3 3)gdbgdb中源码查看相关命令中源码查看相关命令第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4) (4) gdbgdb中查看运行数据相关命令中查看运行数据相关命令第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5 5)其他)其他gdbgdb命令命令 runrun命令:执行当前被调试的程序。命令:执行当前被调试的程序。 killkill命令:停止正在调试的应用程序。命令:停止正在
22、调试的应用程序。 watchwatch命令:设置监视点,监视表达式的变化。命令:设置监视点,监视表达式的变化。 awatchawatch命令:设置读写监视点。当要监视的表达命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与式被读或写时将应用程序挂起。它的语法与watchwatch命令相同。命令相同。 rwatchrwatch命令:设置读监视点,当监视表达式被读命令:设置读监视点,当监视表达式被读时将程序挂起,等侍调试。此命令的语法与时将程序挂起,等侍调试。此命令的语法与watchwatch相同。相同。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式
23、系统设计嵌入式系统设计 info breakinfo break命令:显示当前断点列表,包括每个断命令:显示当前断点列表,包括每个断点到达的次数。点到达的次数。 info filesinfo files命令:显示调试文件的信息。命令:显示调试文件的信息。 info info funcfunc命令:显示所有的函数名。命令:显示所有的函数名。 info localinfo local命令:显示当前函数的所有局部变量的命令:显示当前函数的所有局部变量的信息。信息。 info info progprog命令:显示调试程序的执行状态。命令:显示调试程序的执行状态。 ShellShell命令:执行命令:执
24、行Linux ShellLinux Shell命令。命令。 makemake命令:不退出命令:不退出gdbgdb而重新编译生成可执行文件。而重新编译生成可执行文件。 QuitQuit命令:退出命令:退出gdbgdb。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (6) (6) gdbgdb中修改运行参数相关命令中修改运行参数相关命令gdbgdb可修改运行时的参数,并使该变量按照用户当可修改运行时的参数,并使该变量按照用户当前输入的值继续运行。前输入的值继续运行。方法为:在单步执行的过程中,键入命令:方法为:在单步执行的过程中,键入命令:set s
25、et 变量设定值变量设定值在此之后,程序就会按照该设定的值运行了。在此之后,程序就会按照该设定的值运行了。 特别注意,在特别注意,在gccgcc编译选项中一定要加入编译选项中一定要加入”-g”-g”。只有在代码处于只有在代码处于“运行运行”或或“暂停暂停”状态时才能查看状态时才能查看变量值,设置断点后程序在指定行之前停止。变量值,设置断点后程序在指定行之前停止。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3 GNU make9.3 GNU make命令和命令和makefilemakefile 文件文件 显式规则显式规则 隐晦规则隐晦规则 文
26、件指示。其包括文件指示。其包括3 3个部分,一个是在一个个部分,一个是在一个MakefileMakefile中引用另一个中引用另一个MakefileMakefile,就像,就像C C语言中语言中的的includeinclude一样;另一个是指根据某些情况指定一样;另一个是指根据某些情况指定MakefileMakefile中的有效部分,就像中的有效部分,就像C C语言中的预编译语言中的预编译#if#if一样;还有就是定义一个多行的命令。一样;还有就是定义一个多行的命令。 注释。注释。MakefileMakefile中只有行注释,用中只有行注释,用“#”#”字符字符在在MakefileMakefi
27、le中的命令,必须要以中的命令,必须要以TabTab键开始。键开始。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 GNUGNU的的makemake工作时的执行步骤如下:工作时的执行步骤如下:1. 1. 读入所有的读入所有的MakefileMakefile。2. 2. 读入被读入被includeinclude的其它的其它MakefileMakefile。3. 3. 初始化文件中的变量。初始化文件中的变量。4. 4. 推导隐晦规则,并分析所有规则。推导隐晦规则,并分析所有规则。5. 5. 为所有的目标文件创建依赖关系链。为所有的目标文件创建依赖关系链
28、。6. 6. 根据依赖关系,决定哪些目标要重新生成。根据依赖关系,决定哪些目标要重新生成。7. 7. 执行生成命令。执行生成命令。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.1 9.3.1 MakefileMakefile文件的规则文件的规则 1 1MakefileMakefile书写规则书写规则 MakefileMakefile文件含有一系列的规则,规则内容:文件含有一系列的规则,规则内容: 一个目标(一个目标(targettarget),即),即makemake最终需要创建的文件,最终需要创建的文件,如可执行文件和目标文件;目标也可
29、以是要执行的如可执行文件和目标文件;目标也可以是要执行的动作,如动作,如cleanclean。 一个或多个依赖文件(一个或多个依赖文件(dependencydependency)列表,通常是)列表,通常是编译目标文件所需要的其他文件。编译目标文件所需要的其他文件。 一系列命今一系列命今(command)(command),是,是makemake执行的动作,通常执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行起始字符必须为每个命令占一行,且每个命令行起始字符必须为TABTAB字符。字符。 第第9 9章章 基于基于
30、ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 MakefileMakefile规则的一般形式如下:规则的一般形式如下:targettarget:dependency dependency dependencydependency(tab) (tab) 例如:有以下的例如:有以下的MakefileMakefile文件:文件:testtest:prog.oprog.o code.ocode.ogccgcc o test o test prog.oprog.o code.ocode.oprog.oprog.o:prog.cprog.c prog.hprog.h code.hc
31、ode.hgccgcc c c prog.cprog.c o o prog.oprog.ocode.ocode.o:code.ccode.c code.hcode.hgccgcc c c code.ccode.c o o code.ocode.ocleanclean:rmrm f *.o f *.o 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 一般情况下,调用一般情况下,调用makemake命令可输入:命令可输入:# make target# make targettargettarget是是MakefileMakefile文件中定义的目标之一
32、,如果省文件中定义的目标之一,如果省略略targettarget,makemake就将生成就将生成MakefileMakefile文件中定义的第一个文件中定义的第一个目标。对于上面目标。对于上面MakefileMakefile的例子,单独的一个的例子,单独的一个makemake命令命令等价于:等价于:# make test# make test因为因为testtest是是MakefileMakefile文件中定义的第一个目标,文件中定义的第一个目标,makemake首首先将其读入,然后从第一行开始执行,把第一个目标先将其读入,然后从第一行开始执行,把第一个目标testtest作为它的最终目标,
33、所有后面的目标的更新都会影作为它的最终目标,所有后面的目标的更新都会影响到响到testtest的更新。的更新。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 内核源代码中内核源代码中MakefileMakefile被分布在目录树中,与被分布在目录树中,与MakefileMakefile直接相关的文件有配置文件直接相关的文件有配置文件. .configconfig和规则文件和规则文件Rules.makeRules.make。顶层。顶层MakefileMakefile是整个内核配置、编译的总是整个内核配置、编译的总体控制文件。在顶层体控制文件。在顶层M
34、akefileMakefile中的语句:中的语句:include arch/$(ARCH)/include arch/$(ARCH)/MakefileMakefile包含了特定包含了特定CPUCPU体系结构下的体系结构下的MakefileMakefile,这个,这个MakefileMakefile中包含了平台相关的信息。配置文件中包含了平台相关的信息。配置文件. .configconfig包包含由用户选择项,用来存放内核配置后的结果(如含由用户选择项,用来存放内核配置后的结果(如make make configconfig)。位于各种)。位于各种CPUCPU体系目录下的体系目录下的Makefi
35、leMakefile,比如比如drivers/drivers/MakefileMakefile,负责所在子目录下源代码的管理。,负责所在子目录下源代码的管理。规则文件规则文件Rules.makeRules.make,则被所有的,则被所有的MakefileMakefile使用。使用。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 用户通过用户通过make make configconfig配置后,产生了配置后,产生了. .configconfig。顶层。顶层MakefileMakefile读入读入. .configconfig中的配置选择。顶层中的配
36、置选择。顶层MakefileMakefile有两个有两个主要的任务:产生压缩的内核镜像主要的任务:产生压缩的内核镜像vmlinuxvmlinux文件和内核文件和内核模块模块modulemodule。为了达到此目的,顶层。为了达到此目的,顶层MakefileMakefile递归的进递归的进入到内核的各个子目录中,分别调用位于子目录中的入到内核的各个子目录中,分别调用位于子目录中的MakefileMakefile。至于到底进入哪些子目录,取决于内核的配。至于到底进入哪些子目录,取决于内核的配置。位于各个子目录下的置。位于各个子目录下的MakefileMakefile同样也根据同样也根据. .con
37、figconfig给出给出的配置信息,构造出当前配置下需要的源文件列表,并的配置信息,构造出当前配置下需要的源文件列表,并在文件的最后有在文件的最后有include $(TOPDIR)/ include $(TOPDIR)/ Rules.makeRules.make。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2. 2. 在规则中使用通配符在规则中使用通配符makemake支持支持3 3种通配符:种通配符:“*”“*”,“?”?”和和“.”.”。波浪号(波浪号(“”)字符在文件名中有特殊的用途。如)字符在文件名中有特殊的用途。如“test”te
38、st”表示当前用户的表示当前用户的$HOME$HOME目录下的目录下的testtest目录。而目录。而“ hchenhchen/test”/test”则表示用户则表示用户hchenhchen的宿主目录下的的宿主目录下的 testtest目录。通配符目录。通配符 “* “*.c”.c”表示所有以后缀为表示所有以后缀为c c的文件。的文件。例如:例如:print: *.cprint: *.clprlpr -p $? -p $?touch printtouch print该例说明目标该例说明目标printprint依赖于所有的依赖于所有的.c.c文件。其中的文件。其中的“$?”$?”是一个自动化变量
39、。是一个自动化变量。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3.3.伪目标伪目标伪目标又称假想目标,如:伪目标又称假想目标,如:clean:clean:rmrm *.o temp *.o temp这里并不生成这里并不生成“clean”clean”这个文件。这个文件。“伪目标伪目标”并不并不是一个文件,只是一个标签,由于是一个文件,只是一个标签,由于“伪目标伪目标”不是文件不是文件,所以,所以makemake无法生成它的依赖关系和决定它是否要执行。无法生成它的依赖关系和决定它是否要执行。可使用可使用“make clean”make clean
40、”来使用该目标。来使用该目标。如果你的如果你的MakefileMakefile需要生成若干个可执行文件,可需要生成若干个可执行文件,可把所有的目标文件都写在一个把所有的目标文件都写在一个MakefileMakefile中,可声明了一中,可声明了一个个“all”all”的伪目标。的伪目标。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 声明了一个声明了一个“ “allall” ”的伪目标的伪目标例如:例如:all : prog1 prog2 prog3all : prog1 prog2 prog3prog1 : prog1.o prog1 : pro
41、g1.o utils.outils.occ -o prog1 prog1.o cc -o prog1 prog1.o utils.outils.oprog2 : prog2.oprog2 : prog2.occ -o prog2 prog2.occ -o prog2 prog2.oprog3 : prog3.o prog3 : prog3.o sort.osort.o utils.outils.occ -o prog3 prog3.o cc -o prog3 prog3.o sort.osort.o utils.outils.o第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌
42、入式系统设计嵌入式系统设计 9.3.2 9.3.2 MakefileMakefile文件中隐含规则文件中隐含规则1 1常用的隐含规则常用的隐含规则 编译编译C C程序的隐含规则:程序的隐含规则:.o.o的目标的依赖目标的目标的依赖目标会自动推导为会自动推导为.c.c,并且其生成命令是,并且其生成命令是$(CC) c $(CPPFLAGS) $(CFLAGS)$(CC) c $(CPPFLAGS) $(CFLAGS)。 编译编译C+C+程序的隐含规则:程序的隐含规则:.o.o的目标的依赖目的目标的依赖目标会自动推导为标会自动推导为.cc.cc或是或是.C.C,并且其生成命令是,并且其生成命令是$
43、(CXX) c $(CPPFLAGS) $(CFLAGS)$(CXX) c $(CPPFLAGS) $(CFLAGS)。(建议使用。(建议使用.cc.cc作为作为C+C+源文件的后缀,而不是源文件的后缀,而不是.C.C)第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 汇编和汇编预处理的隐含规则:.o的目标的依赖目标会自动推导为.s,默认使用编译器as,并且其生成命令是:$(AS) $(ASFLAGS)。.s的目标的依赖目标会自动推导为.S,默认使用C预编译器cpp,并且其生成命令是:$(AS) $(ASFLAGS)。 链接Object文件的隐含规则
44、目标依赖于.o,通过运行C 的编译器来运行链接程序生成(一般是ld),其生成命令是:$(CC) $(LDFLAGS) .o $(LOADLIBES) $(LDLIBS)。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)也有效。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2隐含规则使用的变量隐含规则使用的变量(1 1)关于命令的变量。)关于命令的变量。 AR AR :函数库打包程序。默认命令是:函数库打包程序。默认命令是arar。 AS AS :汇编语言编译程序。默认命令是:汇编语言编译程序。默认命令是
45、asas。 CC CC :C C语言编译程序。默认命令是语言编译程序。默认命令是cccc。 CXX CXX :C+C+语言编译程序。默认命令是语言编译程序。默认命令是g+g+。 CPP CPP :C C程序的预处理器(输出是标准输出设备)程序的预处理器(输出是标准输出设备)。默认命令是。默认命令是$(CC) E$(CC) E。 RMRM :删除文件命令。默认命令是:删除文件命令。默认命令是rmrm f f。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2 2)关于命令参数的变量)关于命令参数的变量 以下变量都是相关上面的命令的参数。若没有指明其
46、默以下变量都是相关上面的命令的参数。若没有指明其默认值,则其默认值都是空。认值,则其默认值都是空。 ARFLAGS ARFLAGS :函数库打包程序:函数库打包程序ARAR命令的参数。默认值是命令的参数。默认值是rvrv。 ASFLAGS ASFLAGS :汇编语言编译器参数。(当明显地调用:汇编语言编译器参数。(当明显地调用“.s .s”或或“.S .S”文件时)。文件时)。 CFLAGS CFLAGS :C C语言编译器参数。语言编译器参数。 CXXFLAGS CXXFLAGS :C+C+语言编译器参数。语言编译器参数。 CPPFLAGS CPPFLAGS :C C预处理器参数。(预处理器
47、参数。( C C 和和 Fortran Fortran 编译器也会编译器也会用到)。用到)。 FFLAGS FFLAGS :FortranFortran语言编译器参数。语言编译器参数。 GFLAGS GFLAGS :SCCS getSCCS get程序参数。程序参数。 LDFLAGS LDFLAGS :链接器参数。(如:链接器参数。(如:ld ld)第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3 3自动化变量自动化变量常用的自动化变量如下。常用的自动化变量如下。 $ $ :表示规则中的目标文件集。在模式规则中,:表示规则中的目标文件集。在模式规
48、则中,如果有多个目标,那么,如果有多个目标,那么,$就是匹配于目标中模就是匹配于目标中模式定义的集合。式定义的集合。 $% $% :仅当目标在函数库文件中,表示规则中的目:仅当目标在函数库文件中,表示规则中的目标成员名。例如,如果一个目标是标成员名。例如,如果一个目标是 foo.afoo.a ( (bar.obar.o) ),那么,那么,$%$%就是就是bar.obar.o,$就是就是foo.afoo.a。如果目标不是函数库文件(。如果目标不是函数库文件(UnixUnix下是下是.a.a,WindowsWindows下是下是.lib.lib),那么,其值为空。),那么,其值为空。第第9 9章章
49、 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 $ $ :依赖目标中的第一个目标名字。如果依赖目:依赖目标中的第一个目标名字。如果依赖目标是以模式(即标是以模式(即%)定义的,那么)定义的,那么$将是符合模将是符合模式的一系列的文件集。注意,是一个一个取出来的。式的一系列的文件集。注意,是一个一个取出来的。 $? $? :所有比目标新的依赖目标的集合,以空格分:所有比目标新的依赖目标的集合,以空格分隔。隔。 $ $ :所有的依赖目标的集合,以空格分隔。如果:所有的依赖目标的集合,以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除在依赖目标中有多个重复的
50、,那个这个变量会去除重复的依赖目标,只保留一份。重复的依赖目标,只保留一份。 $+ $+ :这个变量很像:这个变量很像$,也是所有依赖目标的集,也是所有依赖目标的集合。只是它不去除重复的依赖目标。合。只是它不去除重复的依赖目标。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 $* $* :表示不包含扩展名的目标文件名,即目标模:表示不包含扩展名的目标文件名,即目标模式中式中“%”%”及其之前的部分。及其之前的部分。 例:目标是例:目标是dir/dir/a.foo.ba.foo.b,并且目标的模式是,并且目标的模式是a.%.ba.%.b,那么,那么,
51、$*$*的值就是的值就是dir/dir/a.fooa.foo。 如果目标中没有模式的定义,那么如果目标中没有模式的定义,那么$*$*也就不能被推也就不能被推导出。导出。 如果目标文件的后缀是如果目标文件的后缀是make make 所识别的,那么所识别的,那么$*$*就就是除了后缀的那一部分。是除了后缀的那一部分。 例如:如果目标是例如:如果目标是foo.cfoo.c,因为,因为.c.c是是makemake所能识别所能识别的后缀名,所以,的后缀名,所以,$*$*的值就是的值就是foofoo。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 对于上面的七
52、个变量都可以分别加上对于上面的七个变量都可以分别加上D D或是或是F F,表示取文件的目录部分和文件部分。下面以表示取文件的目录部分和文件部分。下面以$为例为例说明其含义:说明其含义: $(D) $(D) :表示:表示$的目录部分(不以斜杠作为结尾)的目录部分(不以斜杠作为结尾),如果,如果$值是值是dir/dir/foo.ofoo.o,那么,那么$(D)$(D)就是就是dirdir,若,若$中没有包含斜杠,其值是中没有包含斜杠,其值是“.”.”(当前目录)。(当前目录)。 $(F) $(F) :表示:表示$的文件部分,如果的文件部分,如果$值是值是dir/dir/foo.ofoo.o,那么,
53、那么$(F) $(F) 就是就是foo.ofoo.o,$(F)$(F)相当于函相当于函数数$($(notdirnotdir $) $)。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.3 9.3.3 MakefileMakefile文件的命令文件的命令 1 1显示命令显示命令echo echo 正在编译正在编译XXXXXX模块模块.当当makemake执行时,会输出执行时,会输出“正在编译正在编译XXXXXX模块模块.”字符串,但不会输出命令,如果没有字符串,但不会输出命令,如果没有“”,那么,那么,makemake将输出:将输出:echo
54、 echo 正在编译正在编译XXXXXX模块模块.正在编译正在编译XXXXXX模块模块.如果如果makemake执行时,带入执行时,带入makemake参数参数-n-n或或-just-print-just-print,则,则只是显示命令,而不会执行命令,这个功能很有利于只是显示命令,而不会执行命令,这个功能很有利于调试调试MakefileMakefile文件。文件。而而makemake参数参数-s -s或或- -slientslient则是全面禁止命令的显示。则是全面禁止命令的显示。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2命令执行命令
55、执行当依赖目标新于目标时,也就是当规则的目标需要当依赖目标新于目标时,也就是当规则的目标需要被更新时,被更新时,makemake会一条一条的执行其后的命令。如果会一条一条的执行其后的命令。如果要让上一条命令的结果应用到下一条命令时,应该使用要让上一条命令的结果应用到下一条命令时,应该使用分号分隔这两条命令。比如第一条命令是分号分隔这两条命令。比如第一条命令是cdcd命令,希望命令,希望第二条命令得在第二条命令得在cdcd之后的基础上运行,那么就不能把这之后的基础上运行,那么就不能把这两条命令写在两行上,而应该把这两条命令写在一行上两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如
56、:,用分号分隔。如:exec:cd/home/hchen;pwd当执行当执行“makeexec”时,时,cd就起作用了,就起作用了,pwd会打印会打印出出“/home/hchen”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3命令出错命令出错在在Makefile命令行前加减号命令行前加减号“-”(在(在Tab键之后),键之后),标记为不管命令出不出错都认为是成功的。例如:标记为不管命令出不出错都认为是成功的。例如:clean:-rm-f*.o若给若给make加上加上-i或是或是-ignore-errors参数,那么,参数,那么,Makefil
57、e中所有命令都会忽略错误。而如果一个规则是中所有命令都会忽略错误。而如果一个规则是以以.IGNORE作为目标的,那么这个规则中的所有命令作为目标的,那么这个规则中的所有命令将会忽略错误。将会忽略错误。若若make的参数的是的参数的是-k或是或是-keep-going,如果某规,如果某规则中的命令出错了,那么就终止该规则的执行,但继续则中的命令出错了,那么就终止该规则的执行,但继续执行其它规则。执行其它规则。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.4 9.3.4 MakefileMakefile文件的变量文件的变量1Makefile中
58、的变量中的变量顶层顶层Makefile定义并向环境中输出了许多变量,并定义并向环境中输出了许多变量,并为各个子目录下的为各个子目录下的Makefile传递一些信息。常用的变量传递一些信息。常用的变量有以下几类:有以下几类:(1)版本信息)版本信息版本信息有版本信息有VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION和和KERNELRELEASE等变量,用来定义当前内核的版本。比如,等变量,用来定义当前内核的版本。比如,VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXTRAVERSION=-rmk7,共同构成内核的发行版,共同构成内核的发行
59、版本本KERNELRELEASE:2.4.18-rmk7。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2)CPU体系结构体系结构ARCH在顶层在顶层Makefile的开头,用的开头,用ARCH定义目标定义目标CPU的体系结构,的体系结构,比如,比如,ARCH:=arm。许多子目录的。许多子目录的Makefile中,要根据中,要根据ARCH的定义选择编译源文件的列表。的定义选择编译源文件的列表。(3)路径信息)路径信息TOPDIR和和SUBDIRSTOPDIR定义了定义了Linux内核源代码所在的根目录。例如,各个内核源代码所在的根目录。例如,
60、各个子目录下的子目录下的Makefile通过通过$(TOPDIR)/Rules.make就可以找到就可以找到Rules.make的位置。的位置。SUBDIRS定义了一个目录列表,在编译内核或模块时,顶层定义了一个目录列表,在编译内核或模块时,顶层Makefile根据根据SUBDIRS来决定进入哪些子目录。来决定进入哪些子目录。SUBDIRS的值的值取决于内核的配置,在顶层取决于内核的配置,在顶层Makefile中中SUBDIRS赋值为赋值为kerneldriversmmfsnetipclib;根据内核的配置情况,在;根据内核的配置情况,在arch/*/Makefile中扩充了中扩充了SUBDI
61、RS的值,可参考的值,可参考arch/arm/Makefile的例子。的例子。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4)内核组成信息)内核组成信息HEAD,CORE_FILES,NETWORKS,DRIVERS,LIBS。Linux内核文件内核文件vmlinux是由以下规则产生的:是由以下规则产生的:vmlinux:$(CONFIGURATION)init/main.oinit/version.olinuxsubdirs$(LD)$(LINKFLAGS)$(HEAD)init/main.oinit/version.o-start-gro
62、up$(CORE_FILES)$(DRIVERS)$(NETWORKS)$(LIBS)-end-group-ovmlinux可以看出,可以看出,vmlinux是由是由HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS和和LIBS组成组成的。的。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 这些变量(如这些变量(如HEAD)都是用来定义链接生成)都是用来定义链接生成vmlinux所需的目标文件和库文件列表。其中,所需的目标文件和库文件列表。其中,HEAD在在arch/arm/Makefile中定义,用
63、来确定最先链接进中定义,用来确定最先链接进vmlinux的文件列表。比如,对于的文件列表。比如,对于ARM系列系列HEAD的定的定义为:义为:HEAD:=arch/arm/kernel/head-$(PROCESSOR).oarch/arm/kernel/init_task.o表明表明head-$(PROCESSOR).o和和init_task.o需要最先被需要最先被链接到链接到vmlinux中。中。PROCESSOR为为armv或或armo,取,取决于目标决于目标CPU。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5 5)编译信息)编译信息
64、CPPCPP,CCCC,ASAS,LDLD,ARAR,CFLAGSCFLAGS,LINKFLAGSLINKFLAGS在在Rules.makeRules.make中定义的是编译的通用规则,具体到特中定义的是编译的通用规则,具体到特定的场合,需明确给出编译环境,编译环境是在以上的定的场合,需明确给出编译环境,编译环境是在以上的变量中定义的。针对交叉编译的要求,定义了变量中定义的。针对交叉编译的要求,定义了CROSS_COMPILECROSS_COMPILE。比如:。比如:CROSS_COMPILECROSS_COMPILE = = arm-arm-linuxlinux- -CC = $(CC =
65、$(CROSS_COMPILE)gccCROSS_COMPILE)gccLD = $(LD = $(CROSS_COMPILE)ldCROSS_COMPILE)ld.第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 由于由于CROSS_COMPILECROSS_COMPILE定义了交叉编译器前缀定义了交叉编译器前缀arm-arm-linuxlinux- -,表明所有的交叉编译工具都是以,表明所有的交叉编译工具都是以arm-arm-linuxlinux- -开头开头的,所以在各个交叉编译器工具之前,都加入了的,所以在各个交叉编译器工具之前,都加入了$(
66、CROSS_COMPILE)$(CROSS_COMPILE),以组成一个完整的交叉编译工具,以组成一个完整的交叉编译工具文件名,比如,文件名,比如,arm-arm-linux-gcclinux-gcc。CFLAGSCFLAGS定义了传递给定义了传递给C C编译器的参数。编译器的参数。LINKFLAGSLINKFLAGS是链接生成是链接生成vmlinuxvmlinux时,由链接器使用的时,由链接器使用的参数。参数。LINKFLAGSLINKFLAGS在在arm/*/ arm/*/ MakefileMakefile中定义,比如:中定义,比如:#arch/arm/#arch/arm/Makefile
67、MakefileLINKFLAGS:=-LINKFLAGS:=-p-X-Tarch/arm/vmlinux.ldsp-X-Tarch/arm/vmlinux.lds 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (6 6)配置变量)配置变量CONFIG_*CONFIG_*. .configconfig文件中有许多的配置变量等式,用来说明用户文件中有许多的配置变量等式,用来说明用户配置的结果。配置的结果。例如,例如,CONFIG_MODULES=y CONFIG_MODULES=y 表明用户选择了表明用户选择了LinuxLinux内核的模块功能。内
68、核的模块功能。. .configconfig被顶层被顶层MakefileMakefile包含后,就形成许多的配置变量,包含后,就形成许多的配置变量,每个配置变量具有确定的值:每个配置变量具有确定的值:y y表示本编译选项对应的表示本编译选项对应的内核代码被静态编译进内核代码被静态编译进LinuxLinux内核;内核;m m表示本编译选项表示本编译选项对应的内核代码被编译成模块;对应的内核代码被编译成模块;n n表示不选择此编译选表示不选择此编译选项;如果没有赋值,那么配置变量的值为空。项;如果没有赋值,那么配置变量的值为空。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式
69、系统设计嵌入式系统设计 2Rules.make变量变量Rules.make定义了所有定义了所有Makefile共用的编译规则。共用的编译规则。Rules.make文件定义了许多编译、链接列表变量。文件定义了许多编译、链接列表变量。O_OBJS、L_OBJS、OX_OBJS和和LX_OBJS:这些变量代:这些变量代表的是本级目录下需要编译进表的是本级目录下需要编译进Linux内核内核vmlinux的目标文件列的目标文件列表,其中表,其中OX_OBJS和和LX_OBJS中的中的“X”表明目标文件使用了表明目标文件使用了EXPORT_SYMBOL输出符号。输出符号。M_OBJS和和MX_OBJS:定
70、义本级目录下需要被编译成可装:定义本级目录下需要被编译成可装载模块的目标文件列表。同样,载模块的目标文件列表。同样,MX_OBJS中的中的“X”表明目标表明目标文件使用了文件使用了EXPORT_SYMBOL输出符号。输出符号。O_TARGET和和L_TARGET:每个子目录下都有一个:每个子目录下都有一个O_TARGET或或L_TARGET,Rules.make首先从源代码编译生成首先从源代码编译生成O_OBJS和和OX_OBJS中所有的目标文件,然后使用中所有的目标文件,然后使用$(LD)-r把它把它们链接成一个们链接成一个O_TARGET或或L_TARGET。O_TARGET以以.o结尾,
71、结尾,而而L_TARGET以以.a结尾。结尾。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3.追加变量值追加变量值可以使用可以使用“+=”操作符给变量追加值,例如:操作符给变量追加值,例如:objects=main.ofoo.obar.outils.oobjects+=another.o于是,于是,$(objects)值变成:值变成:“main.ofoo.obar.outils.oanother.o”,等价于:,等价于:objects=main.ofoo.obar.outils.oobjects:=$(objects)another.o若变量之
72、前没有定义过,那么若变量之前没有定义过,那么“+=”会自动变成会自动变成“=”;若前面有变量定义,那么;若前面有变量定义,那么“+=”会继承于前次会继承于前次操作的赋值符;若前一次的是操作的赋值符;若前一次的是“:=”,那么,那么“+=”会会以以“:=”作为其赋值符。作为其赋值符。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 4目标变量目标变量其语法是:其语法是::可以是各种赋值表达式,如可以是各种赋值表达式,如“=”、“:=”、“+=”或是或是“?=”。第二个语法是针对于第二个语法是针对于make命令行带入的变量,或是命令行带入的变量,或是系统
73、环境变量。这个特性非常的有用,当设置了这样一系统环境变量。这个特性非常的有用,当设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的个变量,这个变量会作用到由这个目标所引发的所有的规则中去。规则中去。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 如:如:prog:CFLAGS=-gprog:prog.ofoo.obar.o$(CC)$(CFLAGS)prog.ofoo.obar.oprog.o:prog.c$(CC)$(CFLAGS)prog.cfoo.o:foo.c$(CC)$(CFLAGS)foo.cbar.o:bar.c$(CC)$
74、(CFLAGS)bar.c在这个示例中,不管全局的在这个示例中,不管全局的$(CFLAGS)$(CFLAGS)的值是什么的值是什么,在,在progprog目标以及其所引发的所有规则中(目标以及其所引发的所有规则中(prog.oprog.o foo.ofoo.o bar.obar.o的规则),的规则),$(CFLAGS)$(CFLAGS)的值都是的值都是“-g -g”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.5 9.3.5 MakefileMakefile文件的条件判断文件的条件判断条件表达式的语法条件表达式的语法1 1:EndifE
75、ndif条件表达式的语法条件表达式的语法2 2:ElseElseEndifEndif第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.5 9.3.5 MakefileMakefile文件的条件判断文件的条件判断例如:例如:bar =bar =foofoo = $(bar) = $(bar)ifdefifdef foofoofrobozzfrobozz = yes = yeselseelsefrobozzfrobozz = no = noendifendif这个例子中,条件为真,这个例子中,条件为真,“ “$($(frobozzfrobozz)
76、 )” ”值是值是“ “yesyes” ”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.6Makefile文件中常用函数文件中常用函数函数的调用语法函数的调用语法函数调用,很像变量的使用,也是以函数调用,很像变量的使用,也是以“$”来标识的,来标识的,其语法如下:其语法如下:$()或是或是:$其中:其中:就是函数名;就是函数名;是函数是函数的参数,参数间以逗号的参数,参数间以逗号“,”分隔,而函数名和参数之分隔,而函数名和参数之间以间以“空格空格”分隔。函数调用以分隔。函数调用以“$”开头,以圆括号开头,以圆括号或花括号把函数名和参数括
77、起。如:或花括号把函数名和参数括起。如:$(substa,b,$(x)。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2字符串函数字符串函数(1 1) 字符串处理函数字符串处理函数$( $(substsubst , ) , )名称:字符串替换函数名称:字符串替换函数substsubst。功能:把字串功能:把字串中的中的字符串替换成字符串替换成。返回:函数返回被替换过后的字符串。返回:函数返回被替换过后的字符串。(2 2)查找字符串函数)查找字符串函数findstringfindstring$( $(findstringfindstring ,
78、 ) , )功能:在字串功能:在字串中查找中查找字串。字串。返回:若找到,那么返回返回:若找到,那么返回,否则返回空字符串。,否则返回空字符串。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3 3)过滤函数)过滤函数filterfilter$(filter,)功能:以功能:以模式过滤模式过滤字符串中的单词,字符串中的单词,保留符合模式保留符合模式的单词。可以有多个模式。的单词。可以有多个模式。返回:返回符合模式返回:返回符合模式的字串。的字串。例如:例如:sources:=foo.cbar.cbaz.sugh.hfoo:$(sources)cc
79、$(filter%.c%.s,$(sources)-ofoo$(filter%.c%.s,$(sources)返回的值是返回的值是“foo.cbar.cbaz.s”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4)反过滤函数)反过滤函数filter-out$(filter-out,)功能:以功能:以模式过滤模式过滤字符串中的单词,字符串中的单词,去除符合模式去除符合模式的单词。可以有多个模式。的单词。可以有多个模式。返回:返回不符合模式返回:返回不符合模式的字串。的字串。示例:示例:objects=main1.ofoo.omain2.obar
80、.omains=main1.omain2.o$(filter-out$(mains),$(objects)返回值是返回值是“foo.obar.o”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5)排序函数)排序函数sort$(sort)功能:给字符串功能:给字符串中的单词排序(升序)。中的单词排序(升序)。返回:返回排序后的字符串。返回:返回排序后的字符串。例如:例如:$(sortfoobarlose)返回返回“barfoolose”。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.3.7子目
81、录子目录Makefile子目录子目录Makefile用来控制本级目录下源代码编译规则。用来控制本级目录下源代码编译规则。下面是一个子目录下面是一个子目录Makefile文件。文件。#Makefileforthelinuxkernel.#Allofthe(potential)objectsthatexportsymbols.#ThislistcomesfromgreplEXPORT_SYMBOL*.hc.export-objs:=tc.o#Objectfilelists.obj-y:=obj-m:=obj-n:=obj-:=第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系
82、统设计嵌入式系统设计 obj-$(CONFIG_TC)+=tc.oobj-$(CONFIG_ZS)+=zs.oobj-$(CONFIG_VT)+=lk201.olk201-map.olk201-remap.o#Filesthatarebothresidentandmodular:removefrommodular.obj-m:=$(filter-out$(obj-y),$(obj-m)#TranslatetoRules.makelists.L_TARGET:=tc.aL_OBJS:=$(sort$(filter-out$(export-objs),$(obj-y)LX_OBJS:=$(sort
83、$(filter$(export-objs),$(obj-y)M_OBJS:=$(sort$(filter-out$(export-objs),$(obj-m)MX_OBJS:=$(sort$(filter$(export-objs),$(obj-m)Include$(TOPDIR)/Rules.make第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.4 9.4 嵌入式嵌入式LinuxLinux引导程序引导程序9.4.1 9.4.1 BootloaderBootloader引导程序引导程序 1 1BootloaderBootloader的架构和
84、功能的架构和功能 BootloaderBootloader的主要功能有:的主要功能有: 初始化初始化CPU CPU 的主频、的主频、SDRAMSDRAM、中断、串口等硬件;、中断、串口等硬件; 启动启动LinuxLinux内核并提供一个内核并提供一个RAMDISKRAMDISK; 通过串口下载内核或通过串口下载内核或RAMDISKRAMDISK到目标板上;到目标板上; 将修改过的内核或将修改过的内核或RAMDISKRAMDISK写入到写入到FlashFlash内;内; 为用户提供一个命令接口。为用户提供一个命令接口。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计
85、嵌入式系统设计 BootloaderBootloader引导程序分为引导程序分为stagelstagel和和stage2stage2两个阶段。两个阶段。(1 1)BootloaderBootloader的的stage1stage1包括以下步骤:包括以下步骤:屏蔽所有的中断屏蔽所有的中断 。为中断提供服务的通常是操作系。为中断提供服务的通常是操作系统统, ,因此在执行因此在执行BootloaderBootloader的过程中可以不响应任何中断。的过程中可以不响应任何中断。中断屏蔽通过写中断屏蔽通过写CPUCPU的中断屏蔽寄存器来完成。的中断屏蔽寄存器来完成。设置设置CPUCPU的时钟频率和速度。
86、的时钟频率和速度。初始化初始化RAMRAM设置系统内存控制器的功能寄存器和各设置系统内存控制器的功能寄存器和各内存库控制寄存器等。内存库控制寄存器等。为加载为加载stage2stage2准备准备RAMRAM空间。空间。拷贝拷贝stage2stage2到到RAMRAM中。中。跳转到跳转到stage2stage2的入口点。的入口点。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2)Bootloader的的stage2stage2的功能是通过串口下载的功能是通过串口下载Linux内核到目标板上。内核到目标板上。包括以下几个步骤:包括以下几个步骤:初始
87、化本阶段要使用到的硬件设备。通常包括:初始化至少初始化本阶段要使用到的硬件设备。通常包括:初始化至少一个串口,以便和终端用户进行一个串口,以便和终端用户进行I/O输出信息;初始化计时器等。输出信息;初始化计时器等。检测系统的内存映射。所谓内存映射就是指在整个检测系统的内存映射。所谓内存映射就是指在整个4GB物理地物理地址空间中有哪些地址范围被分配用来寻址系统的址空间中有哪些地址范围被分配用来寻址系统的RAM单元。单元。加载内核映像和根文件系统从加载内核映像和根文件系统从Flash读入到读入到RAM中。包括两个中。包括两个方面:第一方面是内核映像所占用的内存范围;第二方面是根文方面:第一方面是内
88、核映像所占用的内存范围;第二方面是根文件系统所占用的内存范围。在规划内存占用布局时,主要考虑基件系统所占用的内存范围。在规划内存占用布局时,主要考虑基地址和映像的大小两个方面。地址和映像的大小两个方面。设置内核的启动参数。设置内核的启动参数。调用内核。调用内核。Bootloader调用调用Linux内核的方法是直接跳转到内内核的方法是直接跳转到内核的第一条指令处。核的第一条指令处。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2BootLoader代码分析代码分析下面对引导程序下面对引导程序2410INIT.S2410INIT.S进行分析,以加深
89、进行分析,以加深对对BootLoaderBootLoader的理解。在第一阶段完成依赖于体的理解。在第一阶段完成依赖于体系结构硬件初始化的代码,包括禁止看门狗、禁系结构硬件初始化的代码,包括禁止看门狗、禁止中断、初始化各控制寄存器拷贝自身到止中断、初始化各控制寄存器拷贝自身到RAMRAM等。等。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 IMPORTMainAREAInit,CODE,READONLYENTRYbResetHandlerResetHandlerldrr0,=WTCONldrr1,=0x0strr1,r0ldrr0,=INTMSK
90、ldrr1,=0xffffffffstrr1,r0ldrr0,=INTSUBMSKldrr1,=0x7ffstrr1,r0ldrr0,=LOCKTIMEldr r1,=0xffffffstrr1,r0ldr r0,=SMRDATAldr r1,=BWSCONadd r2,r0,#520ldr r3,r0,#4strr3,r1,#4cmp r2,r0bne %B0第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第二阶段通常用第二阶段通常用C语言实现,包括内存管理单元初始语言实现,包括内存管理单元初始化、时钟设置、端口设置和串口初始化等。化、时钟设置、
91、端口设置和串口初始化等。voidIsr_Init(void)rINTMOD=0x0;/工作在工作在IRQ模式模式rINTMSK=BIT_ALLMSK;/屏蔽中断屏蔽中断rINTSUBMSK=BIT_SUB_ALLMSK;/屏蔽子中断屏蔽子中断第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 voidMain(void)MMU_Init();/MMU初始化初始化ChangeClockDivider(1,1);/设置时钟除法器设置时钟除法器-1:2:4ChangeMPllValue(0xa1,0x3,0x1);/时钟值时钟值FCLK=202.8MHzPo
92、rt_Init();Isr_Init();Uart_Init(0,115200);Uart_Select(0);第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 while(1)Uart_Printf(nnSMDK2410Board(MCUS3C2410)ExampleProgramVer1.0(20020521)FCLK=%dHznn,FCLK);第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.4.2VIVI简介简介VIVIVIVI是韩国是韩国MiziMizi公司开发的公司开发的BootLoader
93、BootLoader,可用于,可用于ARM9ARM9处理器的引导。处理器的引导。VIVIVIVI利用串行通信为用户提供接口。它利用串行通信为用户提供接口。它有如下作用:有如下作用: 把内核把内核(kernel)(kernel)从从flashflash复制到复制到RAMRAM,然后启动它;,然后启动它; 初始化硬件;初始化硬件; 下载程序并写入下载程序并写入flashflash(通常由串口或者网口先把内(通常由串口或者网口先把内核下载到核下载到RAMRAM中,然后写入中,然后写入flashflash);); 检测目标板(检测目标板(bootloaderbootloader会有一些简单的代码用以测
94、会有一些简单的代码用以测试目标板硬件的好坏)。试目标板硬件的好坏)。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 1. VIVI1. VIVI的命令的命令(1 1)loadload命令命令将二进制文件载入到将二进制文件载入到FlashFlash或者或者RAMRAM,命令格式:,命令格式:load load | | 其中:其中: 描述装载位置,有描述装载位置,有flashflash和和ramram两种选项;两种选项; 或或 描述装载的地址,如果有提前描述装载的地址,如果有提前定义的定义的mtdmtd分区信息,可以只输入分区名称,否则需要指定地址和分
95、区信息,可以只输入分区名称,否则需要指定地址和大小;参数大小;参数 确定文件的传输协议,常采用的选项确定文件的传输协议,常采用的选项“x x”用用来指定采用来指定采用xmodemxmodem协议。协议。例如:例如:vivivivi load flash kernel x load flash kernel x,装载压缩映像文件,装载压缩映像文件zImagezImage到到flashflash存储器中,地址是存储器中,地址是kernelkernel分区,并采用分区,并采用xmodemxmodem传输协议。传输协议。例如:例如:vivivivi load flash 0x80000 0xc0000
96、 x load flash 0x80000 0xc0000 x。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2)part命令命令part命令用来操作命令用来操作MTD分区信息,比如显示、增加、删除、复分区信息,比如显示、增加、删除、复位、保存位、保存MTD分区等。分区等。partshow:显示:显示mtd分区信息。分区信息。partadd:增加新的:增加新的mtd分区,分区,其中其中为新为新mtd分区名称,分区名称,是是mtd器件的偏移,器件的偏移,表示表示mtd分区的大小,分区的大小,表示分区类型,可选项有表示分区类型,可选项有JFFS2
97、、LOCKED和和BONFS。partdel:删除一个:删除一个mtd分区。分区。partreset:恢复:恢复mtd分区为默认值。分区为默认值。partsave:在:在flash中永久保存参数值和分区信息。中永久保存参数值和分区信息。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3)param命令命令param命令用来设置或者查看参数。命令用来设置或者查看参数。例如:改变例如:改变linuxcommandline,使用,使用viviparamsetlinux_cmd_line“youwish.”。也可以改变引导程序启动的时间,使用也可以改变引
98、导程序启动的时间,使用viviparamsetboot_delay100000实现。实现。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4)boot命令命令boot命令用来引导存储在命令用来引导存储在flash存储器或者存储器或者ram中的中的linux内核。命令格式:内核。命令格式:boot|参数参数设定存储设定存储linux内核映像的位置,可内核映像的位置,可选项有选项有ram、nor和和smc。参数参数或或描述存储内核的描述存储内核的地址,如果有提前定义的地址,如果有提前定义的mtd分区信息,可以只输入分分区信息,可以只输入分区名称,否则
99、需要指定地址和大小。区名称,否则需要指定地址和大小。例如:例如:vivibootnor0x80000表示从表示从flash存储器中读出存储器中读出linux内核,偏移是内核,偏移是0x80000。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5)flash命令命令flash命令是存储器管理命令,命令是存储器管理命令,例如:例如:flasherase|表示擦除表示擦除flash存储器。存储器。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2VIVI的目录树的目录树 VIVI的目录如图的目录如图9.
100、1所示所示第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2VIVIVIVI的目录树的目录树 VIVIVIVI包括的目录具体如下:包括的目录具体如下: archarch:包括所有:包括所有VIVIVIVI支持的目标板的子目录。支持的目标板的子目录。 DocumentationDocumentation:存放了许多文档,包括:存放了许多文档,包括VIVIVIVI使用使用指南。指南。 driversdrivers:其中包括了引导内核所需的:其中包括了引导内核所需的MTDMTD设备和设备和串口驱动程序。串口驱动程序。MTDMTD目录下分目录下分map
101、smaps、nandnand和和nornor三个三个目录,实现对目录,实现对NandNand Flash Flash和和Nor FlashNor Flash的读写控制。的读写控制。SerialSerial目录下的文件实现对串口的控制,并支持目录下的文件实现对串口的控制,并支持xmodemxmodem和和ymodemymodem协议。协议。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 includeinclude:头文件的公共目录,其中有:头文件的公共目录,其中有S3C2410.hS3C2410.h。Platform/smdk2410.hPlatf
102、orm/smdk2410.h定义了与目标板相关的资源配置定义了与目标板相关的资源配置参数,修改波特率、引导参数和物理内存映射等参数参数,修改波特率、引导参数和物理内存映射等参数即可配置目标板。即可配置目标板。 initinit:此目录只有:此目录只有main.cmain.c和和version.cversion.c两个文件。与两个文件。与普通的普通的C C程序一样,程序一样,VIVIVIVI将从将从mainmain函数开始执行。函数开始执行。 liblib:一些平台公共的接口代码,比如,:一些平台公共的接口代码,比如,time.ctime.c里里的的udelayudelay() ()和和mdel
103、aymdelay() ()。 scriptsscripts:此目录存放了配置所需的脚本文件,如:此目录存放了配置所需的脚本文件,如MenuconfigMenuconfig和和ConfigureConfigure文件,以方便对文件,以方便对VIVIVIVI的配置。的配置。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 9.5 9.5 嵌入式嵌入式Linux Linux 下程序调试应用举例下程序调试应用举例9.5.1 Linux9.5.1 Linux宿主机下的应用程序调试宿主机下的应用程序调试下面用一个例子来介绍下面用一个例子来介绍Linux开发工具
104、开发工具GNUgcc的的编译和编译和GNUgdb调试器的使用。调试器的使用。1 1 gccgcc编译器的使用编译器的使用假定例中有假定例中有4 4个文件,分别是个文件,分别是hello.hhello.h、starfun.hstarfun.h、hello.chello.c和和star.cstar.c。使用。使用ViVi编辑器完成编辑器完成hello.hhello.h、starfun.hstarfun.h、hello.chello.c和和star.cstar.c 4 4个文件的内容输入。个文件的内容输入。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计
105、Starfun.h文件内容如下:文件内容如下:/*starfun.h*/#ifndefSTARFUN_H#defineSTARFUN_H#defineNUM4#defineNUMBER3intstar1()inti,j,k;for(k=1;k=NUM;+k)for(i=1;i=(NUM-k);+i)printf();for(j=1;j=0;-k)for(i=1;i=(NUMBER-k+1);+i)printf();for(j=1;j=(2*k-1);+j)printf(*);printf(n);return0;#endif第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系
106、统设计嵌入式系统设计 hello.h文件内容如下:文件内容如下:/*hello.h*/#ifndefHELLO_H#defineHELLO_Hvoidhello()star1();printf(hello,myfriendsn);#endifhello.c文件内容如下:文件内容如下:voidshowhello()hello();star.c文件内容如下:文件内容如下:#includestarfun.h#includehello.h#includeintmain()star1();star2();showhello();return0;第第9 9章章 基于基于ARM9ARM9和和LinuxLin
107、ux嵌入式系统设计嵌入式系统设计 分析上述分析上述4 4个程序,掌握嵌入式个程序,掌握嵌入式LinuxLinux下下C C程序程序的的宏定义用法,例如:宏定义用法,例如:#ifndefSTARFUN_H#defineSTARFUN_H对于上述对于上述4 4个程序,有多种编译方法,常用的方个程序,有多种编译方法,常用的方法有分步进行编译、用一条命令完成编译、用法有分步进行编译、用一条命令完成编译、用MakefileMakefile文件进行编译、使用动态链接库进行编译、文件进行编译、使用动态链接库进行编译、使用静态链接库进行编译等。编译命令中,若没有使用静态链接库进行编译等。编译命令中,若没有-g
108、 -g参数,生成的目标程序只能运行而不能调试。参数,生成的目标程序只能运行而不能调试。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第一种方法:分步进行编译第一种方法:分步进行编译编译步骤如下:编译步骤如下:(1)由)由star.cstarfun.h文件生成文件生成star.o目标文件,目标文件,命令如下:命令如下:gcc-g-cstar.c-ostar.o(2)由)由hello.chello.hstarfun.h生成生成hello.o目标目标文件,命令如下:文件,命令如下:gcc-g-chello.c-ohello.o(3)由)由hello.o
109、star.o生成应用程序生成应用程序myprog,命命令如下:令如下:gccstar.ohello.o-omyprog第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 运行目标程序运行目标程序myprog的命令如下:的命令如下:rootlocalhost01_hello#./myprog可在终端窗口下显示运行结果。可在终端窗口下显示运行结果。*hello,myfriends第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第二种方法:一条命令完成编译第二种方法:一条命令完成编译可用一条命令完成以上操作,编译
110、命令如下:可用一条命令完成以上操作,编译命令如下:gccgcc star.cstar.c hello.chello.c -o -o myprogmyprog第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第三种方法:用第三种方法:用MakefileMakefile文件完成编译文件完成编译可在可在ViVi下编辑下编辑MakefileMakefile文件,然后用文件,然后用makemake命令命令执行执行MakefileMakefile文件。下面是用于本例的两个文件。下面是用于本例的两个MakefileMakefile文件,第一个是不带参数文件,第一个
111、是不带参数-g-g的简单的简单MakefileMakefile文件;第二个是带参数(包括文件;第二个是带参数(包括-g-g参数)参数)的的MakefileMakefile文件。文件。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 #不带参数不带参数-g的的Makefile文件文件myprog:star.ohello.ogccstar.ohello.o-omyprogstar.o:star.cstarfun.hgcc-cstar.c-ostar.ohello.o:hello.chello.hstarfun.hgcc-chello.c-ohello.o
112、#带参数带参数-g的的Makefile文件文件OBJS=star.ohello.oCC=gccCFLAGS=-Wall-O-gEXEC=myprog$(EXEC):$(OBJS)$(CC)$(OBJS)-o$(EXEC)star.o:star.cstarfun.h$(CC)$(CFLAGS)-cstar.c-ostar.ohello.o:hello.chello.hstarfun.h$(CC)$(CFLAGS)-chello.c-ohello.oclean:rm-f*.o.out$(EXEC) 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第四种
113、方法:使用动态库完成编译第四种方法:使用动态库完成编译 使用动态库编译就是把其中的某个或某些源使用动态库编译就是把其中的某个或某些源程序编译成动态库,然后将其拷贝到程序编译成动态库,然后将其拷贝到/ /usrusr/lib/lib文件文件夹中(夹中(/ /usrusr/lib/lib是用户库自动搜索路径)。最后把是用户库自动搜索路径)。最后把动态库与其他待编译的源程序一同编译连接成可动态库与其他待编译的源程序一同编译连接成可执行的目标程序。执行的目标程序。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 步骤如下:步骤如下: (1 1)用)用gcc
114、gcc编译器生成形成动态库所用到的编译器生成形成动态库所用到的hello.ohello.o文件文件rootlocalhost01_hello#gcc-c-fpichello.crootlocalhost01_hello#lshello.chello.hhello.omakefile_01makefile_02makefile_03Makefile_rulestar.cstarfun.h第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2)用)用gcc编译器生成动态库编译器生成动态库libhello.so文件文件rootlocalhost01_hel
115、lo#gcc-shared-s-olibhello.sohello.orootlocalhost01_hello#lsamakehello.chello.hhello.olibhello.somakefile_01makefile_02makefile_03Makefile_rulestar.cstarfun.h注意注意libhello.so库文件的命名格式,(库文件的命名格式,(1)()(2)也可以)也可以用下边命令替代用下边命令替代gcc-fpic-shared-shello.c-olibhello.so第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系
116、统设计 (3)拷贝库文件到)拷贝库文件到/usr/lib文件夹中文件夹中rootlocalhost01_hello#cplibhello.so/usr/lib注意注意/usr/lib为用户库自动搜索路径为用户库自动搜索路径(4)动态库与其他待编译的源程序一同编译连接成可执行的目)动态库与其他待编译的源程序一同编译连接成可执行的目标程序标程序rootlocalhost01_hello#gcc-lhellostar.c-omystarrootlocalhost01_hello#lddmystarlibhello.so=/usr/lib/libhello.so(0x4002d000)libc.so.
117、6=/lib/tls/libc.so.6(0x42000000)/lib/ld-linux.so.2=/lib/ld-linux.so.2(0x40000000)(5)运行可执行的目标程序)运行可执行的目标程序rootlocalhost01_hello#./mystar可得与第一种方法同样的结果。可得与第一种方法同样的结果。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 第五种方法:使用静态库完成编译第五种方法:使用静态库完成编译在使用静态库编译之前应先删除已有对应的目标文件在使用静态库编译之前应先删除已有对应的目标文件和库文件,然后再使用静态库编
118、译,和库文件,然后再使用静态库编译,步骤如下:步骤如下:(1)删除已有对应的目标文件和库文件)删除已有对应的目标文件和库文件rootlocalhost01_hello#rm*.orm:是否删除一般文件:是否删除一般文件hello.o?yrootlocalhost01_hello#rmmystarrm:是否删除一般文件:是否删除一般文件mystar?yrootlocalhost01_hello#rootlocalhost01_hello#rmlibhello.*第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2)把)把hello.c编译连接成静态库
119、编译连接成静态库rootlocalhost01_hello#gcc-chello.c-ohello.orootlocalhost01_hello#ar-rclibhello.ahello.o(3)静态库与其他待编译的源程序一同编译连接成可)静态库与其他待编译的源程序一同编译连接成可执行的目标程序执行的目标程序rootlocalhost01_hello#gccstar.clibhello.a-omystar(4)运行可执行的目标程序)运行可执行的目标程序rootlocalhost01_hello#./mystar可得与第一种方法同样的结果。可得与第一种方法同样的结果。第第9 9章章 基于基于AR
120、M9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 2 2LinuxLinux宿主机下的宿主机下的GNU GNU gdbgdb调试器的使用调试器的使用 下面通过用下面通过用gdbgdb调试一个语法上没有错误,但功能调试一个语法上没有错误,但功能上有错误的程序来说明上有错误的程序来说明gdbgdb的使用。的使用。代码示例代码示例 eg1.ceg1.c#include #include intint wib(intwib(int no1, no1, intint no2) no2) intint result, diff; result, diff; diff = no1 - no2
121、; diff = no1 - no2; result = no1 / diff; result = no1 / diff; return result; return result; 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 intint main(intmain(int argcargc, char *, char *argvargv) intint value, div, result, i, total; value, div, result, i, total; value = 10; value = 10; div = 6; div
122、 = 6; total = 0; total = 0; for(ifor(i = 0; i 10; i+) = 0; i 10; i+) result = result = wib(valuewib(value, div);, div); total += result; total += result; div+; div+; value-; value-; printf(%dprintf(%d wibedwibed by %d equals % by %d equals %dndn, value, div, total);, value, div, total); return 0; re
123、turn 0; 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 这个程序将运行这个程序将运行 10 10 次次 for for 循环,使用循环,使用 wibwib() () 函数计算出累积值,最后打印出结果。使用函数计算出累积值,最后打印出结果。使用 gccgcc -g eg1.c -o eg1 -g eg1.c -o eg1 进行编译,并用进行编译,并用 gdbgdb eg1 eg1 启启动动 gdbgdb。使用。使用 run run 运行程序可能会产生以下消运行程序可能会产生以下消息:息:Program received signal SIGF
124、PE, Arithmetic exception.Program received signal SIGFPE, Arithmetic exception.0x80483ea in 0x80483ea in wibwib (no1=8, no2=8) at eg1.c:7 (no1=8, no2=8) at eg1.c:77 result = no1 / diff;7 result = no1 / diff;( (gdbgdb) )第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 gdbgdb 指出在程序第指出在程序第 7 7 行发生一个算术异常,通
125、常它会打印这行发生一个算术异常,通常它会打印这一行以及一行以及 wibwib() () 函数的自变量值。要查看第函数的自变量值。要查看第 7 7 行前后的源代码,行前后的源代码,请使用请使用 list list 命令,它通常会打印命令,它通常会打印 10 10 行。再次输入行。再次输入 listlist(或者(或者按回车重复上一条命令)将列出程序的下按回车重复上一条命令)将列出程序的下 10 10 行。从行。从 gdbgdb 消息消息中可以看出,第中可以看出,第 7 7 行中的除法运算出了错,程序在这一行中将行中的除法运算出了错,程序在这一行中将变量变量 no1 no1 除以除以 diffdi
126、ff。要查看变量的值,使用要查看变量的值,使用 gdbgdb print print 命令并指定变量名。输入命令并指定变量名。输入 print no1 print no1 和和 print diffprint diff,可以相应看到,可以相应看到 no1 no1 和和 diff diff 的值,结的值,结果如下:果如下:( (gdbgdb) print no1) print no1$5 = 8$5 = 8( (gdbgdb) print diff) print diff$2 = 0$2 = 0第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 gdb指
127、出指出no1等于等于8,diff等于等于0。根据这些值和第。根据这些值和第7行中的语句,我们可以推断出算术异常是由除数为行中的语句,我们可以推断出算术异常是由除数为0的除法运的除法运算造成的。清单显示了第算造成的。清单显示了第6行计算的变量行计算的变量diff,我们可以打,我们可以打印印diff表达式(使用表达式(使用printno1-no2命令),来重新估计这命令),来重新估计这个变量。个变量。gdb告诉我们告诉我们wib函数的这两个自变量都等于函数的这两个自变量都等于8,于,于是我们要检查调用是我们要检查调用wib()函数的函数的main()函数,以查看这是在函数,以查看这是在什么时候发生
128、的。在允许程序自然终止的同时,我们使用什么时候发生的。在允许程序自然终止的同时,我们使用continue命令告诉命令告诉gdb继续执行。继续执行。(gdb)continueContinuing.ProgramterminatedwithsignalSIGFPE,Arithmeticexception.Theprogramnolongerexists.第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (1)使用断点)使用断点为了查看在为了查看在main()中发生了什么情况,可以在程中发生了什么情况,可以在程序代码中的某一特定行或函数中设置断点,这样序代
129、码中的某一特定行或函数中设置断点,这样gdb会在遇到断点时中断执行。可以使用命令会在遇到断点时中断执行。可以使用命令breakmain在进入在进入main()函数时设置断点,或者可以指定函数时设置断点,或者可以指定其它任何感兴趣的函数名来设置断点。然而,我们只其它任何感兴趣的函数名来设置断点。然而,我们只希望在调用希望在调用wib()函数之前中断执行。输入函数之前中断执行。输入listmain将打印从将打印从main()函数开始的源码清单,再次按回车函数开始的源码清单,再次按回车将显示第将显示第21行上的行上的wib()函数调用。要在那一行上函数调用。要在那一行上设置断点,只需输入设置断点,只
130、需输入break21。 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 gdb将发出以下响应:将发出以下响应:(gdb)break21Breakpoint1at0x8048428:fileeg1.c,line21.以显示它已在我们请求的行上设置了以显示它已在我们请求的行上设置了1号断点。号断点。run命令命令将从头重新运行程序,直到将从头重新运行程序,直到gdb中断为止。发生这种情况时,中断为止。发生这种情况时,gdb会生成一条消息,指出它在哪个断点上中断,以及程序运会生成一条消息,指出它在哪个断点上中断,以及程序运行到何处:行到何处:Breakp
131、oint1,main(argc=1,argv=0xbffff954)ateg1.c:2121result=wib(value,div);发出发出printvalue和和printdiv将会显示在第一次调用将会显示在第一次调用wib()时,变量分别等于时,变量分别等于10和和6,而,而printi将会显示将会显示0。幸好,。幸好,gdb将显示所有局部变量的值,并使用将显示所有局部变量的值,并使用infolocals命令保存大量命令保存大量输入信息输入信息 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 gdb将发出以下响应:将发出以下响应:(gdb)
132、break21Breakpoint1at0x8048428:fileeg1.c,line21.以显示它已在我们请求的行上设置了以显示它已在我们请求的行上设置了1号断点。号断点。run命令命令将从头重新运行程序,直到将从头重新运行程序,直到gdb中断为止。发生这种情况时,中断为止。发生这种情况时,gdb会生成一条消息,指出它在哪个断点上中断,以及程序运会生成一条消息,指出它在哪个断点上中断,以及程序运行到何处:行到何处:Breakpoint1,main(argc=1,argv=0xbffff954)ateg1.c:2121result=wib(value,div);发出发出printvalue和
133、和printdiv将会显示在第一次调用将会显示在第一次调用wib()时,变量分别等于时,变量分别等于10和和6,而,而printi将会显示将会显示0。幸好,。幸好,gdb将显示所有局部变量的值,并使用将显示所有局部变量的值,并使用infolocals命令保存大量命令保存大量输入信息输入信息 第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 从以上的调查中可以看出,当从以上的调查中可以看出,当 value value 和和 div div 相等时相等时就会出现问题,因此输入就会出现问题,因此输入 continue continue 继续执行,直到下一次
134、继续执行,直到下一次遇到遇到 1 1 号断点。对于这次迭代,号断点。对于这次迭代,info locals info locals 显示了显示了 value=9 value=9 和和 div=7div=7。 与其再次继续,还不如使用与其再次继续,还不如使用 next next 命令单步调试程序,命令单步调试程序,以查看以查看 value value 和和 div div 是如何改变的。是如何改变的。gdbgdb 将响应:将响应:( (gdbgdb) next) next22 total += result; 22 total += result; 再按两次回车将显示加法和减法表达式:再按两次回车
135、将显示加法和减法表达式:( (gdbgdb) )23 div+;23 div+;( (gdbgdb) )24 value-;24 value-;第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 再按两次回车将显示第再按两次回车将显示第 21 21 行,行,wibwib() () 调用。调用。info locals info locals 将显示目前将显示目前 div div 等于等于 valuevalue,这就意味着将发生问题。如果有兴趣,可以使用这就意味着将发生问题。如果有兴趣,可以使用 step step 命令(与命令(与 next next 形
136、成对比,形成对比,next next 将跳过将跳过函数调用)来继续执行函数调用)来继续执行 wibwib() () 函数,以再次查看除函数,以再次查看除法错误,然后使用法错误,然后使用 next next 来计算来计算 resultresult。 完成调试后,可以使用完成调试后,可以使用 quit quit 命令退出命令退出 gdbgdb。由于程序仍在运行,这个操作会终止它,由于程序仍在运行,这个操作会终止它,gdbgdb 将提示将提示确认。确认。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2 2)更多断点和观察点的设置)更多断点和观察点的设
137、置在第在第 21 21 行中设置断点。行中设置断点。要设置条件,可以在定义断点时指定要设置条件,可以在定义断点时指定 “ “break break if ” if ”。将将 eg1 eg1 再次装入再次装入 gdbgdb,并输入:,并输入:(gdb)break21ifvalue=divBreakpoint1at0x8048428:fileeg1.c,line21.如果已经在第如果已经在第 21 21 行中设置了断点,如行中设置了断点,如 1 1 号断点,号断点,则可以使用则可以使用 condition condition 命令来代替在断点上设置命令来代替在断点上设置条件:条件: ( (gdbg
138、db) condition 1 value=div) condition 1 value=div第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 使用使用 condition condition 命令时,如果指定断点编号但命令时,如果指定断点编号但又不指定表达式,可以将断点设置成无条件断点,例又不指定表达式,可以将断点设置成无条件断点,例如,如,condition 1 condition 1 就将就将 1 1 号断点设置成无条件断点。号断点设置成无条件断点。要查看当前定义了什么断点及其条件,请发出命令要查看当前定义了什么断点及其条件,请发出命令 in
139、fo breakinfo break:(gdb)infobreakNumTypeDispEnbAddressWhat1breakpointkeepy0x08048428inmainateg1.c:21stoponlyifvalue=divbreakpointalreadyhit1time第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 下面三个命令可以对断点操作:下面三个命令可以对断点操作:禁止断点禁止断点命令:命令:disable启用断点命令:启用断点命令: enablebreakpointnumber删除断点命令:删除断点命令:delete第第9
140、 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 设置监视点:设置监视点:监视点将中断程序执行,须在表达式监视点将中断程序执行,须在表达式中所使用的变量在作用域中时设置监视点。要获取作中所使用的变量在作用域中时设置监视点。要获取作用域中的用域中的 value value 和和 divdiv,可以在,可以在 main main 函数上设函数上设置断点,然后运行程序,当遇到置断点,然后运行程序,当遇到 main() main() 断点时设置断点时设置监视点。重新启动监视点。重新启动 gdbgdb,并装入,并装入 eg1eg1,然后输入:,然后输入:(gdb)b
141、reakmainBreakpoint1at0x8048402:fileeg1.c,line15.(gdb)run.Breakpoint1,main(argc=1,argv=0xbffff954)ateg1.c:1515value=10;第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 要了解要了解 div div 何时更改,可以使用何时更改,可以使用 watch watch divdiv,但由于要在,但由于要在 div div 等于等于 value value 时中断,那时中断,那么应输入:么应输入:如果继续执行,那么当表达式如果继续执行,那么当表达
142、式 div=value div=value 的的值从值从 0 0(假)变成(假)变成 1 1(真)时,(真)时,gdbgdb 将中断:将中断:(gdb)continueContinuing.Hardwarewatchpoint2:div=valueOldvalue=0Newvalue=1main(argc=1,argv=0xbffff954)ateg1.c:1919for(i=0;i10;i+)第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 info locals info locals 命令将验证命令将验证 “value” value” 是否确实
143、等于是否确实等于 “ “div”div”(再次(再次声明,是声明,是 8 8)。)。info watch info watch 命令将列出已定义的监命令将列出已定义的监视点和断点(此命令等价于视点和断点(此命令等价于 info info breakbreak),而且可以使用与断点相同的语),而且可以使用与断点相同的语法来启用、禁用和删除监视点。法来启用、禁用和删除监视点。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3 3)core core 文件文件gdbgdb 可装入可装入 core core 文件,检查程序中止之前的状态。文件,检查程序中
144、止之前的状态。在在 gdbgdb 外运行示例程序外运行示例程序eg1eg1将会导致核心信息转储:将会导致核心信息转储:$ ./eg1$ ./eg1Floating point exception (core dumped) Floating point exception (core dumped) 要使用要使用 core core 文件启动文件启动 gdbgdb,在,在 shell shell 中发出命令中发出命令 gdbgdb eg1 core eg1 core 或或 gdbgdb eg1 -c core eg1 -c core。gdbgdb 将装将装入入 core core 文件,文件,
145、eg1 eg1 的程序清单,显示程序是如何终止的程序清单,显示程序是如何终止的,并显示非常类似于我们刚才在的,并显示非常类似于我们刚才在 gdbgdb 下运行程序时看下运行程序时看到的消息:到的消息:第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 .Corewasgeneratedby./eg1.Programterminatedwithsignal8,Floatingpointexception.#00x80483eainwib(no1=8,no2=8)ateg1.c:77result=no1/diff;此时,可以发出此时,可以发出infoloc
146、als、print、infoargs和和list命令来查看引起除数为零的值。命令来查看引起除数为零的值。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4 4)堆栈跟踪)堆栈跟踪要打印堆栈,发出命令要打印堆栈,发出命令 btbt(backtracebacktrace的缩写:的缩写:(gdb)bt#00x80483eainwib(no1=8,no2=8)ateg1.c:7#10x8048435inmain(argc=1,argv=0xbffff9c4)ateg1.c:21此结果显示了在此结果显示了在 main() main() 的第的第 21 21
147、 行中调用了函行中调用了函数数 wibwib()()(只要使用(只要使用 list 21 list 21 就能证实这一点),就能证实这一点),而且而且 wibwib() () 在在 0 0 号帧中,号帧中,main() main() 在在 1 1 号帧中。由号帧中。由于于 wibwib() () 在在 0 0 号帧中,那么它就是执行程序时发生号帧中,那么它就是执行程序时发生算术错误的函数。算术错误的函数。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 info locals info locals 命令,命令,gdbgdb 会打印出当前帧中的局部变
148、量,会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(缺省情况下,这个帧中的函数就是被中断的函数(0 0 号帧)。号帧)。可以使用命令可以使用命令 frame frame 打印当前帧。要查看打印当前帧。要查看 main main 函数(在函数(在 1 1 号帧中)中的变量,可以发出号帧中)中的变量,可以发出 frame 1 frame 1 切换到切换到 1 1 号帧,然号帧,然后发出后发出 info locals info locals 命令:命令:(gdb)frame1#10x8048435inmain(argc=1,argv=0xbffff9c4)ateg1.c:21
149、21result=wib(value,div);(gdb)infolocalsvalue=8div=8result=4i=2total=6此信息显示了在第三次执行此信息显示了在第三次执行for循环时(循环时(i等于等于2)发生了)发生了错误,此时错误,此时value等于等于div。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5 5)连接到其它进程)连接到其它进程除了调试除了调试 core core 文件或程序之外,文件或程序之外,gdbgdb 还可以连接到已经运还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中行的进程(
150、它的程序已经过编译,并加入了调试信息),并中断该进程。只需用希望断该进程。只需用希望 gdbgdb 连接的进程标识替换连接的进程标识替换 core core 文件名文件名就可以执行此操作。以下是一个执行循环并睡眠的就可以执行此操作。以下是一个执行循环并睡眠的 示例程序示例程序: eg2示例代码示例代码#includeintmain(intargc,char*argv)inti;for(i=0;i60;i+)sleep(1);return0;第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 使用使用 gccgcc -g eg2.c -o eg2 -g
151、eg2.c -o eg2 编译该程序并使用编译该程序并使用 ./eg2 & ./eg2 & 运行该程序。请留意在启动该程序时在背景上打印的进程标识,运行该程序。请留意在启动该程序时在背景上打印的进程标识,在本例中是在本例中是 12831283:./eg2 &./eg2 &3 12833 1283启动启动 gdbgdb 并指定进程标识,在我举的这个例子中是并指定进程标识,在我举的这个例子中是 gdbgdb eg2 1283eg2 1283。gdbgdb 会查找一个叫作会查找一个叫作 1283 1283 的的 core core 文件。如果文件。如果没有找到,那么只要进程没有找到,那么只要进程 1
152、283 1283 正在运行(在本例中可能在正在运行(在本例中可能在 sleep() sleep() 中),中),gdbgdb 就会连接并中断该进程:就会连接并中断该进程:./home/seager/gdb/1283:Nosuchfileordirectory.Attachingtoprogram:/home/seager/gdb/eg2,Pid1283.0x400a87f1in_libc_nanosleep()from/lib/libc.so.6第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (gdb)此时,可以发出所有常用此时,可以发出所有常用
153、gdbgdb 命令。可以使用命令。可以使用 backtracebacktrace 来查看当前位置与来查看当前位置与 main() main() 的相对关系,以及的相对关系,以及 mianmian() () 的帧号的帧号是什么,然后切换到是什么,然后切换到 main() main() 所在的帧,查看已经在所在的帧,查看已经在 for for 循循环中运行了多少次:环中运行了多少次:(gdb)backtrace#00x400a87f1in_libc_nanosleep()from/lib/libc.so.6#10x400a877din_sleep(seconds=1)at./sysdeps/uni
154、x/sysv/linux/sleep.c:78#20x80483efinmain(argc=1,argv=0xbffff9c4)ateg2.c:7(gdb)frame2#20x80483efinmain(argc=1,argv=0xbffff9c4)ateg2.c:77sleep(1);(gdb)printi$1=50第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (6 6)其它小技巧)其它小技巧gdbgdb 可以让您通过使用可以让您通过使用 shell shell 命令在不退命令在不退出调试环境的情况下运行出调试环境的情况下运行 shell sh
155、ell 命令,调用形命令,调用形式是式是 shell shell commandlinecommandline,这有助于在调,这有助于在调试时更改源代码。试时更改源代码。最后,在程序运行时,可以使用最后,在程序运行时,可以使用 set set 命令修改变量的值。在命令修改变量的值。在 gdbgdb 下再次运行下再次运行 eg1eg1,使用命令使用命令 break 7 if diff=0 break 7 if diff=0 在第在第 7 7 行行(将在此处计算结果)设置条件断点,然后运(将在此处计算结果)设置条件断点,然后运行程序。当行程序。当 gdbgdb 中断执行时,可以将中断执行时,可以将
156、 “diff” diff” 设置成非零值,使程序继续运行直至设置成非零值,使程序继续运行直至结束。结束。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 调试过程如下:调试过程如下:Breakpoint1,wib(no1=8,no2=8)ateg1.c:77result=no1/diff;(gdb)printdiff$1=0(gdb)setdiff=1(gdb)continueContinuing.0wibedby16equals10Programexitednormally.第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统
157、设计嵌入式系统设计 9.5.2 9.5.2 目标机下的应用程序调试目标机下的应用程序调试1交叉编译器交叉编译器首先在首先在Linux宿主机上安装北京博创的宿主机上安装北京博创的ARM2410-S嵌入式系嵌入式系统开发平台的系统软件,交叉编译器统开发平台的系统软件,交叉编译器arm4vl-unknown-linux-gcc和和arm4vl-unknown-linux-gdb等文件都是存放在等文件都是存放在/opt/host/arm4vl/bin/文件夹中。另外,用文件夹中。另外,用Vi编辑器编辑编辑器编辑/root/.bash_profile文件,使该文件中文件,使该文件中PATH环境变量包括环
158、境变量包括/opt/host/arm4vl/bin/路径,即:路径,即:PATH=$PATH:$HOME/bin:/opt/host/arm4vl/bin/否则,应把否则,应把/opt/host/arm4vl/bin/添加到添加到PATH环境变量中,环境变量中,使用交叉编译器时能自动找到交叉编译器使用交叉编译器时能自动找到交叉编译器arm4vl-unknown-linux-gcc。.bash_profile文件存盘后执行一次下面命令:文件存盘后执行一次下面命令:source/root/.bash_profile第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式
159、系统设计 2 2编译编译下面是使用了交叉编译器的下面是使用了交叉编译器的MakefileMakefile文件。文件。#Makefile文件文件TOPDIR=./include$(TOPDIR)Rules.makEXEC=$(INSTALL_DIR)/hello./helloOBJS=hello.oall:$(EXEC)$(EXEC):$(OBJS)$(CC)$(LDFLAGS)-o$(OBJS)install:$(EXP_INSTALL)$(EXEC)$(INSTALL_DIR)clean:-rm-f$(EXEC)*.elf*.gdb*.o第第9 9章章 基于基于ARM9ARM9和和Linux
160、Linux嵌入式系统设计嵌入式系统设计 在在MakefileMakefile文件中有文件中有include $(include $(TOPDIR)Rules.makTOPDIR)Rules.mak语句,是包括了上一层的通用规则文件,语句,是包括了上一层的通用规则文件,Rules.makRules.mak文文件如下:件如下:#Rules.mak文件文件TOPDIR=.CROSS=arm4vl-unknown-linux-CC=$CROSSgccgLDFLAGS+=staticEXTRA_LIBS+=EXP_INSTALL=installm755INSTALL_DIR=./bin建立上面文件后,就
161、可对建立上面文件后,就可对hello.chello.c进行交叉编译了。进行交叉编译了。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 先检查原来有无对先检查原来有无对hello.chello.c编译过,即有编译过,即有无无* *.o.o和可执行目标文件,若编译过,则先删和可执行目标文件,若编译过,则先删除除* *.o.o和可执行目标文件,然后执行和可执行目标文件,然后执行makemake命令。命令。rootlocalhost01_hellomakecleanrootlocalhost01_hello第第9 9章章 基于基于ARM9ARM9和和Lin
162、uxLinux嵌入式系统设计嵌入式系统设计 3 3从目标机上挂载从目标机上挂载linuxlinux宿主机中的共享文件夹宿主机中的共享文件夹从目标机上挂载从目标机上挂载linuxlinux宿主机中的共享文件夹的步宿主机中的共享文件夹的步骤如下:骤如下:(1 1)从)从linuxlinux宿主机的宿主机的“主菜单主菜单/ /系统设置系统设置/ /服务器服务器设置设置/ /服务服务”中把中把iptableiptable的勾去掉,而勾选的勾去掉,而勾选nfsnfs项。项。(2 2)设置共享文件夹)设置共享文件夹(3 3)进入目标机)进入目标机(4 4)在目标机上挂载宿主机的共享目录)在目标机上挂载宿主
163、机的共享目录(5 5)在目标机上建立存放待调试程序的目录,并)在目标机上建立存放待调试程序的目录,并把宿主机(虚拟机)上共享目录中已交叉编译生成的把宿主机(虚拟机)上共享目录中已交叉编译生成的待调试的可执行程序拷贝到该目录中。待调试的可执行程序拷贝到该目录中。第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 3 3从目标机上挂载从目标机上挂载linuxlinux宿主机中的共享文件夹宿主机中的共享文件夹从目标机上挂载从目标机上挂载linuxlinux宿主机中的共享文件夹的步宿主机中的共享文件夹的步骤如下:骤如下:(1 1)从)从linuxlinux宿主
164、机的宿主机的“主菜单主菜单/ /系统设置系统设置/ /服务器服务器设置设置/ /服务服务”中把中把iptableiptable的勾去掉,而勾选的勾去掉,而勾选nfsnfs项。项。(2 2)设置共享文件夹)设置共享文件夹(3 3)进入目标机)进入目标机(4 4)在目标机上挂载宿主机的共享目录)在目标机上挂载宿主机的共享目录(5 5)在目标机上建立存放待调试程序的目录,并)在目标机上建立存放待调试程序的目录,并把宿主机(虚拟机)上共享目录中已交叉编译生成的把宿主机(虚拟机)上共享目录中已交叉编译生成的待调试的可执行程序拷贝到该目录中。待调试的可执行程序拷贝到该目录中。第第9 9章章 基于基于ARM
165、9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2 2)设置共享文件夹)设置共享文件夹设置共享文件夹的步骤是:设置共享文件夹的步骤是:从从linux宿主机的宿主机的“主菜单主菜单/系统设置系统设置/服务器设置服务器设置/NFS服务器服务器”进入添加共享目录窗口。主要设置以下项:进入添加共享目录窗口。主要设置以下项: 基本选项:目录:基本选项:目录:/01_hello /01_hello (可通过浏览选择(可通过浏览选择需设置的共享目录)需设置的共享目录) 主机:主机:192.168.0.* 192.168.0.* (为所有段内的主机,即(为所有段内的主机,即192.168.0
166、.0192.168.0.0192.168.0.255192.168.0.255) 常规选项:勾选常规选项:勾选“允许来自高于允许来自高于10241024的端口连接的端口连接”和和“按要求同步写按要求同步写”复选项。复选项。 用户访问选项:勾选用户访问选项:勾选“把远程根用户当作本地根把远程根用户当作本地根用户用户”选项。选项。返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3 3)进入目标机)进入目标机可从可从linuxlinux宿主机或宿主机或WindowsWindows的超级终端进入目标机。的超级终端进入目标机。从从linuxlinu
167、x宿主机上进入目标机的方法:宿主机上进入目标机的方法:在在linuxlinux宿主机的终端窗口中键入宿主机的终端窗口中键入 minicomminicom 回车。回车。注意,在启动注意,在启动minicomminicom前应先把目标机的电源关掉,当前应先把目标机的电源关掉,当minicomminicom启动后,按启动后,按Ctrl + ACtrl + A,再分别按,再分别按Z Z和和O O,即可按要求配置,即可按要求配置minicomminicom的串行口。配置完串行口后,打开目标机电源,按回车的串行口。配置完串行口后,打开目标机电源,按回车键即可进入目标机。键即可进入目标机。从从Windows
168、Windows的超级终端进入目标机的方法:的超级终端进入目标机的方法:在进入在进入WindowsWindows的超级终端前应先把目标机的电源关掉,当的超级终端前应先把目标机的电源关掉,当进入进入WindowsWindows的超级终端后,打开目标机电源,按回车键即可进的超级终端后,打开目标机电源,按回车键即可进入目标机。注意,在进入入目标机。注意,在进入WindowsWindows的超级终端时也要设置串行口,的超级终端时也要设置串行口,若此时在若此时在linuxlinux宿主机上使用过串行口,则应把宿主机上使用过串行口,则应把linuxlinux虚拟机的虚拟机的串行口断开连接。串行口断开连接。返
169、回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (4 4)在目标机上挂载宿主机的共享目录)在目标机上挂载宿主机的共享目录在目标机上执行下列命令,要从目标机上挂在目标机上执行下列命令,要从目标机上挂载宿主机的共享目录。载宿主机的共享目录。mounttnfsonolock192.168.0.42:/01_hello/host其中,其中,192.168.0.42192.168.0.42为为linuxlinux宿主机(或宿主机(或linuxlinux虚拟机)的虚拟机)的IPIP地址,地址,/01_hello/01_hello为宿主机为宿主机上的共享目
170、录,上的共享目录,/host /host 为目标机的挂载目录。为目标机的挂载目录。返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (5 5)在目标机上建立存放待调试程序的目录,)在目标机上建立存放待调试程序的目录,并把宿主机(虚拟机)上共享目录中已交叉编并把宿主机(虚拟机)上共享目录中已交叉编译生成的待调试的可执行程序拷贝到该目录中,译生成的待调试的可执行程序拷贝到该目录中,方法如下:方法如下:/mnt/yaffsmkdirzxt/mnt/yaffschmod777zxt/mnt/yaffscdzxt/mnt/yaffs/zxtcp/hos
171、t/01_hello/*.*zxt也可以只拷贝可执行的目标程序到也可以只拷贝可执行的目标程序到zxtzxt目录中。目录中。返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 4 4在开发板上进行远程调试在开发板上进行远程调试(1 1)在开发上启动)在开发上启动gdbservergdbserver 网口网口 串口串口(2 2)在宿主机(虚拟机)上启动交叉调试器)在宿主机(虚拟机)上启动交叉调试器(3 3)启动远程调试功能)启动远程调试功能第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (1 1)在开发
172、上启动)在开发上启动gdbservergdbserver最最好好先先把把gdbservergdbserver文文件件拷拷贝贝到到目目标标机机中中待待调调试试程程序序的的目目录录中中,如如/ /mnt/yaffs/zxtmnt/yaffs/zxt的的文文件件夹夹中中,在在目目标标机机(开开发发板板)上上启启动动gdbservergdbserver工工具具,可可采采用用两两种种不不同同的连接方式。的连接方式。 网口网口 串口串口返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 网口网口通过网口连接方式启动通过网口连接方式启动gdbservergd
173、bserver工具,可通过下命工具,可通过下命令行完成。令行完成。/mnt/yaffs/zxt./gdbserver192.168.0.42:1234myprogProcessmyprogcreated;pid=105其中:其中:192.168.0.42192.168.0.42是宿主机的是宿主机的IPIP地址,地址,12341234是端是端口。口。myprogmyprog是目标机上待调试程序,是目标机上待调试程序,hellohello前可包含路径,前可包含路径,待调试程序待调试程序hellohello应与宿主机(虚拟机)上共享目录中应与宿主机(虚拟机)上共享目录中已交叉编译生成的待调试程序是同
174、一程序。已交叉编译生成的待调试程序是同一程序。返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 串口串口通过串口连接方式启动通过串口连接方式启动gdbservergdbserver工具,可通工具,可通过下命令行完成。过下命令行完成。/mnt/yaffs/zxt./gdbserver/dev/tts/0helloProcesshellocreated;pid=112Remotedebuggingusing/dev/tts/0其中:其中:/dev/tts/0/dev/tts/0是开发板上的串行口号。是开发板上的串行口号。返回返回第第9 9章章 基
175、于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (2 2)在宿主机(虚拟机)上启动交叉调试器)在宿主机(虚拟机)上启动交叉调试器下面命令行可在宿主机(虚拟机)上启动交叉调试下面命令行可在宿主机(虚拟机)上启动交叉调试器,进入器,进入gdbgdb,并装入,并装入myprogmyprog程序。程序。rootlocalhostarm4vl-unknown-linux-gdb/01_hello/myprog其中,其中,arm4vl-unknown-linux-gdbarm4vl-unknown-linux-gdb是交叉调试器,是交叉调试器,/01_hello/myprog/
176、01_hello/myprog是共享目录中待调试的程序。是共享目录中待调试的程序。返回返回第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 (3 3)启动远程调试功能)启动远程调试功能 进入进入gdbgdb后,同样可分别采用网络和串口两种连接方后,同样可分别采用网络和串口两种连接方式启动远程调试功能。下面的通过网络连接方式启动远式启动远程调试功能。下面的通过网络连接方式启动远程调试功能。程调试功能。( (gdb)targetgdb)target remote 192.168.0.115:1234 remote 192.168.0.115:1234Re
177、mote debugging using 192.168.0.115:1234Remote debugging using 192.168.0.115:12340x40000d00 in ? ()0x40000d00 in ? ()( (gdbgdb) )其中,其中,192.168.0.115:1234192.168.0.115:1234是目标机的是目标机的IPIP地址与端口。地址与端口。下面命令行可启用串行口方式调试。下面命令行可启用串行口方式调试。( (gdb)targetgdb)target remote /dev/ttyS0 remote /dev/ttyS0第第9 9章章 基于基于ARM9ARM9和和LinuxLinux嵌入式系统设计嵌入式系统设计 上上述述步步骤骤完完成成后后,就就可可使使用用gdbgdb的的各各命命令令对对程程序序进进行行单单步步、断断点点、连连续续调调试试,利利用用工工作作环环境境相相关关命命令令、设设置置断断点点与与恢恢复复命命令令、源源代代码码查查看看命命令令、查查看看运运行行数数据据相相关关命命令令及及修修改改运运行行参参数数命命令令等等进进行行高高效效的的程程序序调调试试,与与linuxlinux宿宿主主机机上上gdbgdb命令和使用方法完全相同。命令和使用方法完全相同。 返回返回