【文档说明】第4章嵌入式Linux程序设计基础课件.ppt,共(70)页,539.512 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-55024.html
以下为本文档部分文字说明:
第4章嵌入式Linux程序设计基础LOGO本章要点•1、嵌入式Linux编译器GCC•2、“文件包含”处理•3、make命令和Makefile文件•4、使用autotools系列工具•5、位运算单击此处编辑母版副标题样式LOGO4.1
嵌入式Linux编译器•Linux下C语言编译过程•GCC编译器LOGOLinux下C语言编译过程•Linux下的C语言程序设计与在其他环境中的C程序设计一样,主要涉及编辑器、编译链接器、调试器及项目管理工具。•(1)编辑器.–Lin
ux下的编辑器就如Windows下的记事本、写字板等一样,完成对所录入文字的编辑功能。Linux中最常用的编辑器有vi(vim)和emacs,LOGO•(2)编译链接器。–编译是指源代码转化生成可执行代码的过程,它所完成的主要工作如图所示。编译过程是非常复杂的,它包括词法、语法和语义
的分析、中间代码的生成和优化、符号表的管理和出错处理等。在Linux中,最常用的编译器是gcc编译器。•(3)调试器–调试器并不是代码执行的必备工具,而是专为方便程序员调试程序而用的。gdb是绝大多数Linux开发人员所使用的调试器,它可以方便地设置断点、单步跟踪等。•(4)项目管理器。–
Linux中的项目管理器“make”有些类似于Windows中Visualc++里的“工程”,它是一种控制编译或者重复编译软件的工具,另外,它还能自动管理软件编译的内容、方式和时机,使程序员能够把精力集中在代码的编写上而不是在源代
码的组织上。LOGOGCC编译器•Linux系统下的gcc(GNUCCompiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作之一。gcc可以在多种硬体平台上编译出可执行程序,其执行效率与一般的编译器相比平均效率要高20%~30%。•GCC编译器能将C、C++语言源程
序、汇编程序编译、链接成可执行文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。LOGOGCC编译器•使用GCC编译程序时,编译过程可以被细分为四个阶段:–预处理(Pre-Processing)–编译(Co
mpiling)–汇编(Assembling)–链接(Linking)LOGOGCC编译器•gcc通过后缀来区别输入文件的类别:–.c为后缀的文件:C语言源代码文件–.a为后缀的文件:是由目标文件构成
的库文件–.C,.cc或.cxx为后缀的文件:是C++源代码文件–.h为后缀的文件:头文件–.i为后缀的文件:是已经预处理过的C源代码文件–.ii为后缀的文件:是已经预处理过的C++源代码文件–.o为后缀的文件:是编译后的目标
文件–.s为后缀的文件:是汇编语言源代码文件–.S为后缀的文件:是经过预编译的汇编语言源代码文件。LOGO起步(演示)•hello.c:•#include<stdio.h>•intmain(void)•{•prin
tf(Helloworld!\n);•return0;•}•编译和运行这段程序:•#gcchello.c-ohello•#./hello•输出:Helloworld!LOGOGcc的基本用法和选项•Gcc最基本的用法∶•gcc[options][filenames]–-c:只编
译,不连接成为可执行文件。–-ooutput_filename:确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。–-g:产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。–-O:
对程序进行优化编译、链接,采用这个选项,整个源代码会在编译、链接过程中进行优化处理,产生的可执行文件的执行效率较高。–-O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。LOGO编译选项(optimize.c)•#include<stdio.h>•intmain(void)•{
•doublecounter;•doubleresult;•doubletemp;•for(counter=0;counter<2000.0*2000.0*2000.0/20.0+2020;•counter+=(5-1)/4){•temp=counter/1979;•result=c
ounter;•}•printf(Resultis%lf\\n,result);•return0;•}LOGO•1.gccoptimize.c-ooptimizetime./optimize•2.gcc–Ooptimize.c-ooptimizetime./optimize•对比两次执行的输出结
果不难看出,程序的性能的确得到了很大幅度的改善LOGO•-static:静态链接库文件•例:gcc–statichello.c-ohello•库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀。例如:libhello.solibhello.a。当使用静态库时,连接器找出程序所需的函数,然
后将它们拷贝到可执行文件,一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样,动态库会在执行程序内留下一个标记‘指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库。•演
示:静态链接与动态链接可执行文件大小比较LOGO•-Wall:生成所有警告信息•-w:不生成任何警告信息LOGO4.2“文件包含”处理•1、头文件•在C语言中,需要利用头文件来定义结构、常量以及声明函数的原型。大多数C的头文件都存
放在/usr/include及其子目录下。•引用以上目录中的头文件在编译的时候无需加上路径,但如果程序中引用了其他路径的头文件,需要在编译的时候用–I参数。LOGO4.2“文件包含”处理•-Idirname:将dirname所指出的目录加入到程序头文件目录列表中。•C程序中的头文件包含两种情况∶
–#include<A.h>–#include“B.h”•对于<>,预处理程序cpp在系统预设的头文件目录(如/usr/include)中搜寻相应的文件;而对于””,cpp在当前目录中搜寻头文件。这个选项的作用是告诉cpp,如果在当前目录中没有找到需要的文件,就到指定的dirname目
录中去寻找。•例:gccfoo.c-I/home/include-ofooLOGO•-lname:在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或者由-L选项确定的目录下。例如,-lm表示连接名为“libm.a”的数学函数库。
•例:gccfoo.c-L/home/lib-lfoo-ofooLOGO•2、“文件包含”处理•“文件包含”处理,意思是把另外一个源文件的内容包含到本程序中来。其作用是减少编写程序的重复劳动,即把一些要重复使用的东西,编写到一个“头文件”(*.h)中,然后在程序中
用#include命令来实现“文件包含”的操作。LOGO•例如:∑n=1+2+3+……+100求和运算。•1#include<stdio.h>•2intmain(){•3intx=100,s=0,i=1;•5while(i<=x){•6s=s+i;•
7i++;•8}•9printf("sum=%d\n",s);•10return0;•11}LOGO•为了让加法部分能重复使用,将加法部分写成一个函数intsum(intn)。•intmysum(intn)•{•inti=1,ss=0;•while(i<=n){•ss
=ss+i;•i++;•}•return(ss);•}LOGO•再在主函数中调用它•1#include<stdio.h>•2intmysum(intn);•3intmain()•4{•5intx=100;•6ints=0;•7s=mysum(x);•8printf("sum=%
d\n",s);•9return0;•10}LOGO•注意:上述程序中的第2行语句•intmysum(intn);•是必不可少的。由于mysum(intn)函数的定义是从第11行语句开始,而调用mysum(intn)函数的语句在第7行。因此,要在调用之前声明这
个函数。•下面进一步将程序中具有独立功能的mysum()函数分割出来。该程序可分割为下列3个程序:mysum.h、mysum.c和ex_sum.c。LOGO•(1)程序mysum.h:–1/*mysum.
h*/–2intmysum(intn);•(2)程序mysum.c:–1./*mysum.c*/–2.intmysum(intn)–3.{–4.inti=1,ss=0;–5.while(i<=n){–6.ss=ss+i;–7.i++;–8
.}–9.return(ss);–10.}LOGO•主程序ex_sum.c:•1./*ex_sum.c*/•2.#include<stdio.h>•3.#include"mysum.h"•4.intmain()•5.{•6.intx=100;•7.ints=0;•8.s
=mysum(x);•9.printf("sum=%d\n",s);•10.return0;•11.}LOGO•在Linux环境下,执行编译程序命令:•gccex_sum.cmysum.c-osum•此命令将ex_sum.c和mysum.c编译成一个在Linux环境下的可执行文件su
m。•在Linux环境下运行可执行文件sum,•./sum•结果如下:•sum=5050LOGO4.3Make命令和Makefile工程管理•Linux程序员必须学会使用GNUmake来构建和管理自己的软件工程。GNU的
make能够使整个软件工程的编译、链接只需要一个命令就可以完成。•make在执行时,需要一个命名为Makefile的文件。Makefile文件描述了整个工程的编译,连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译;需要创建那些库文件以及如何创建这些库文件、如何
最后产生我们想要得可执行文件。LOGO•Makefile是一个配置文件。•Makefile中通常包含如下内容:用于说明如何生成一个或多个目标文件规则–需要由make工具创建的目标体,通常是目标文件或可执行文件;–要创建的目标体所依赖的文件;–创
建每个目标体时需要运行的命令。LOGO•Makefile的格式为:targets:prerequisitescommand•目标依赖命令main.o:main.cgcc–cmain.c•目标?依赖?命令?**命令需要以【TAB
】键开始**LOGO认识Make•编写一个Makefile文件如下:•sum:ex_sum.omysum.o•gccex_sum.omysum.o-osum•ex_sum.o:ex_sum.c•gcc-cex_s
um.c•mysum.o:mysum.cmysum.h•gcc-cmysum.cLOGO•注意,“gccex_sum.cmysum.c-osum”前面不是空格,而是按下“tab”键的符号位。•我们将其保存为:makefile,文件
名没有后缀。然后,在Linux环境下执行make,其运行结果如下:•#make•gcc-cex_sum.c•gcc-cmysum.c•gccex_sum.omysum.o-osum•将ex_sum.c和mys
um.c编译成在Linux环境下的可执行文件sum。LOGO•在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条
规则中的目标将被确立为最终的目标。LOGO伪目标•Makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phonytargets)。.PHONY:cleanclean:rm–fhellomain.ofunc1.ofunc
2.o•“.PHONY”将“clean”目标声明为伪目标LOGO文件名•make命令默认在当前目录下寻找名字为makefile或者Makefile的工程文件,当名字不为这两者之一时,可以使用如下方法指定:•make–f文件名LOGO示例•该程序有mytool1.h、mytool2.
h、mytool1.c、mytool2.c等文件需要编译。编写Makefile文件:•main:main.omytool1.omytool2.o•gcc-omainmain.omytool1.omytool2.o•main.o:main.c•gc
c-cmain.c•mytool1.o:mytool1.cmytool1.h•gcc-cmytool1.c•mytool2.o:mytool2.cmytool2.h•gcc-cmytool2.cLOGO•再次运行make,这时,make会自动检查相关文件的时间戳。•首
先,在检查“main”、“main.o”、“mytool1.o”和“mytool2.o”这3个文件的时间戳之前,它会向下查找那些把“main.o”、“mytool1.o”或“mytool2.o”作为目标文件的时间戳。如果这些文件中任何一个的时间戳比它们新,则用gcc
命令将此文件重新编译。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。这也就是Make工作的基本流程。LOGOMakefile变量hello:main.ofunc1.ofunc2.ogccmain.ofunc1.ofunc2.o-ohello•思考1:如果要为h
ello目标添加一个依赖,如:func3.o,该如何修改?•答案1:hello:main.ofunc1.ofunc2.ofunc3.ogccmain.ofunc1.ofunc2.ofunc3.o-ohello•答案2:obj=main.ofunc1.ofunc2.ofun
c3.ohello:$(obj)gcc$(obj)-ohelloLOGOMakefile变量•为了进一步简化编辑和维护Makefile,make允许在Makefile中创建和使用变量。变量是在Makefile中定义
的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。•在Makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。•递归展开方式的定义格式为:VAR=var。简单扩展方式的
定义格式为:VAR:=var。•Make中的变量无论采用哪种方式定义使用时格式均为:$(VAR)。LOGO例如•OBJS=main.omytool1.omytool2.o•CC=gcc•main:$(OBJS)•$(C
C)$(OBJS)-omain•main.o:main.c•$(CC)-cmain.c•mytool1.o:mytool1.cmytool1.h•$(CC)-cmytool1.c•mytool2.o:mytool2.cmytool2.h•$(CC)-cm
ytool2.cLOGO•在makefile中,存在系统默认的自动化变量–$^:代表所有的依赖文件–$@:代表目标–$<:代表第一个依赖文件•例:hello:main.ofunc1.ofunc2.ogccmain.ofunc1.ofunc2.o-ohello=》hello:main.of
unc1.ofunc2.ogcc$^-o$@LOGO进一步简化•OBJS=main.omytool1.omytool2.o•CC=gcc•main:$(OBJS)•$(CC)$^-o$@•main.o:main.c•$(CC)-c$<-o$@
•mytool1.o:mytool1.cmytool1.h•$(CC)-c$<-o$@•mytool2.o:mytool2.cmytool2.h•$(CC)-c$<-o$@LOGO•Makefile中
“#”字符后的内容被视作注释。hello:hello.c@gcchello.c–ohello•@:取消回显LOGO隐式规则(ImplicitRule)•在我们使用Makefile时,有一些语句经常使用,而且使用频率非常高的东西,隐式规则能够告诉make使用默认的方式来完成编译任务,这样,当用
户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。Make会自动按隐式规则来确定如何生成目标文件。OBJS=main.omytool1.omytool2.oCC=gccmain:$(OBJS)$(CC)$^
-o$@LOGO模式规则(PatternRule)•模式规则规定,在目标文件的定义时需要用“%”字符。“%”的意思是表示一个或多个任意字符,与文件名匹配。•例如:“%.c”表示以“.c”结尾的文件名(文件名的长度至
少为3),而“s.%.c”则表示以“s.”开头,“.c”结尾的文件名(文件名的长度至少为5个字符)。OBJS=main.omytool1.omytool2.oCC=gccmain:$(OBJS)$(CC)$^-o$@%.o:
%.c$(CC)-c$<-o$@LOGO•如果一个目标在Makefile中的所有规则都没有命令列表,make会尝试在内建的隐含规则(ImplicitRule)数据库中查找适用的规则。make的隐含规则数据库可以用make-p命令打印,打印出来的格式也是Mak
efile的格式,包括很多变量和规则。LOGO•#default•OUTPUT_OPTION=-o$@•#default•CC=cc•#default•COMPILE.c=$(CC)$(CFLAGS)$(CPPFLAGS)$(TARGET_ARCH)-c•%.o:%.c•#comman
dstoexecute(built-in):•$(COMPILE.c)$(OUTPUT_OPTION)$<LOGO•#号在Makefile中表示单行注释,就像C语言的//注释一样。CC是一个Makefile变量,用CC=cc定义和赋值,用$(CC)取它的值,其值应该是c
c。Makefile变量像C的宏定义一样,代表一串字符,在取值的地方展开。cc是一个符号链接,通常指向gcc,在有些UNIX系统上可能指向另外一种C编译器。•CFLAGS这个变量没有定义,$(CFLAGS)展开是空,CPPFLAGS和TARGET_ARCH也是如此。这样$(COMPILE.c)展开
应该是cc␣空␣空␣空␣-c,去掉所有的“空”得到cc␣␣␣␣-c,注意中间留下4个空格,所以%.o:%.c规则的命令$(COMPILE.c)␣$(OUTPUT_OPTION)␣$<展开之后是cc␣␣␣␣-c␣-o␣$@␣$<,和上面的编译命令已经很接近了。LOGO•%.o
:%.c是一种特殊的规则,称为模式规则(PatternRule)。现在回顾一下整个过程,在我们的Makefile中以hello.o为目标的规则都没有命令列表,所以make会查找隐含规则,发现隐含规则中有这样一条模式规则适用,
hello.o符合%.o的模式,现在%就代表hello(称为hello.o这个名字的Stem),再替换到%.c中就是hello.c。LOGOGDB程序调试•GDB是GNU发布的一款功能强大的程序调试工具。GDB主要完成下面三个方面的功能:–1、启动被调试
程序。–2、让被调试的程序在指定的位置停住。–3、当程序被停住时,可以检查程序状态(如变量值)。LOGO起步(tst.c)•#include<stdio.h>•voidmain()•{•inti;•longresult=0;•for(i=1;i<=100;i+
+)•{•result+=i;•}•printf("result=%d\n",result);•}LOGOGDB快速进阶•1.编译生成可执行文件:gcc-gtst.c-otst•2.启动GDBgdbtst•3.在main函数处设置断点breakmai
n•4.运行程序run•5.单步运行next•6.继续运行continueLOGO启动GDB•1.gdb调试程序名例:gdbhelloworld•2.gdbfile调试程序名LOGOGDB命令•list(l)查看程序•break
(b)函数名在某函数入口处添加断点•break(b)行号在指定行添加断点•break(b)文件名:行号在指定文件的指定行添加断点•break(b)行号if条件当条件为真时,指定行号处断点生效,例b5if
i=10,当i等于10时第5行断点生效LOGOGDB命令•infobreak查看所有设置的断点•delete断点编号删除断点•run(r)开始运行程序•next(n)单步运行程序(不进入子函数)•step(s
)单步运行程序(进入子函数)•continue(c)继续运行程序•print(p)变量名查看指定变量值•finish运行程序,直到当前函数结束•watch变量名对指定变量进行监控•quit(q)退出gdbLOGO4.5位运算LOGO4.5.1位运算符•C语言提供了
六种位运算符:&(按位与)、|(按位或)、^(按位异或)、~(取反)、<<(左移)、>>(右移)。LOGO1、按位与运算•位运算符•按位与运算:只有对应的两个二进位均为1时,结果位才为1,否则为0。参与运算的
数以补码方式出现。例如:9&5可写成算式如下:•可见9&5=1。LOGO•按位与运算通常用来对某些位清0或保留某些位。•例如把a的高八位清0,保留低八位,可作a&0x00ff运算(0x00ff的二进制数为000
0000011111111)。main(){inta=9,b=5,c;c=a&b;printf("a=%d/nb=%d/nc=%d/n",a,b,c);}LOGO2、按位或运算•只要对应的二个二进位有一个为1时,结果
位就为1。•例如:9|5可写算式如下:•可见9|5=13。LOGO•main(){inta=9,b=5,c;c=a|b;printf("a=%d/nb=%d/nc=%d/n",a,b,c);}LOGO3、按位异或
运算•当两对应的二进位相异时,结果为1。•例如9^5:•main(){inta=9;a=a^15;printf("a=%d/n",a);}LOGO4、求反运算•其功能是对参与运算的数的各二进位按位求反。•
例如,~9的运算为:~(0000000000001001)结果为:1111111111110110。LOGO5、左移运算•把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。LOGO例如:a<<4LOGO6、右移运算•把“>>”左边的运算数的各二进
位全部右移若干位,“>>”右边的数指定移动的位数。•例如:设a=15,a>>2表示把000001111右移为00000011(十进制3)。LOGO4.5.2位表达式•将位运算符连接起来所构成的表达式称为位表达式。在这些位运算符中,其优先级依次为:•~(取反运算符)、<<
或>>(左移或右移)、&(按位与)或|(按位或)或^(按位异或)。LOGO•a<<=5;•就等价于:•a=a<<5;•再比如:GPDR&=~0xff;•我们将其展开:GPDR=GPDR&(~0xff);GPDR=GPDR&0x00;•完成了对GPDR的清0。LOGO•一个常用
的操作是用&来获取某个或者某些位。•例如获取整数x中的低4位可以写成x&=0x0F;x=x&0x0F;LOGO•也可以用|、&、<<、>>等配合来设置和清除某位或者某些位。•例如:x&=0x1;•即:x=x&0x1;/*清除x的最后
一位,即第0位*/x&=(0x1<<5);•即:x=x&(0x1<<5);/*清除x的低5位*/•x|=0x1;•即:x=x|0x1;/*将最后一位(即第0位)设置为1*/•x|=(0x1<<6);•即:x=x|(0x1<<6);/*将x的第6位设置为1*/LOGO本
章小结•本章是在嵌入式Linux中进行程序设计的基础,介绍了Gcc编译器的使用,虽然它的选项较多,但掌握常用的一些选项即可。介绍了Make工程管理器的使用,这里包括Makefile的基本结构、Makefile的变量定义及其规则和mak
e命令的使用。还介绍了autotools的使用,这在项目设计中是非常有用的工具。最后简单介绍了位运算。感谢您的关注