第-2-章-嵌入式Linux开发基础_第1页
第-2-章-嵌入式Linux开发基础_第2页
第-2-章-嵌入式Linux开发基础_第3页
第-2-章-嵌入式Linux开发基础_第4页
第-2-章-嵌入式Linux开发基础_第5页
已阅读5页,还剩105页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第2章嵌入式Linux开发根底目录Linux程序的编译和交叉编译

1嵌入式Linux动态库和静态库的制作与应用

2Makefile根底和应用3嵌入式Linux远程调试

4嵌入式Liunx多线程应用程序设计

5嵌入式Linux下C和汇编的混合编程

6嵌入式Linuxsocket网络编程根底72.1Linux程序的编译和交叉编译

1.

gcc编译器简介

2.

gcc的执行过程

3.gcc的根本用法和选项

4.

gcc的错误类型分析

2.1.1gcc编译器简介

在linux下最为常用的C编译器就是gcc。gcc是GUNCompilerCollection的简称,是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一,成为Linux平台编译器的事实标准。同时,在Linux平台下的嵌入式开发领域,gcc也是用得最普遍的一种编译器。gcc之所以被广泛采用,是因为它能支持各种不同的目标体系结构。gcc编译器能将C、C++语言源程序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc那么通过后缀来区别输入文件的类别。2.1.2gcc的执行过程

预处理编译汇编连接cppcc1asld2.1.3gcc的根本用法和选项gcc的根本用法gcc

[options]

[filenames]

2.gcc的常用选项-c-o

output_filename-g-O-O2-Idirname-Ldirname-lname-Wall-shared-static2.1.3gcc的根本用法和选项gcc编译实例:1.编译单个源文件gcc-ohellohello.c2.编译多个源文件gcc-omessagemessage.cmain.c3.使用外部函数库gcc-otrig-lmtrig.c2.1.4gcc的错误类型分析

1.C语法错误

2.头文件错误

3.档案库错误

4.未定义符号

2.2嵌入式Linux动态库和静态库的制作与应用

Linux静态库和动态库

1

静态库的制作和应用

2

动态库的制作和应用

32.2.1Linux静态库和动态库

Linux函数库分为静态库和动态库两种。1.静态函数库这类库的名字一般是libxxx.a,其中xxx表示库的名称。例如:静态库名为message,那么静态库文件名就是libmessage.a。利用静态函数库编译成的文件比较大,因为整个静态库在程序编译时会被连接到目标代码中,它的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为它的缺点,因为如果静态函数库改变了,那么程序必须重新编译。2.动态函数库这类库的名字一般是libxxx.so,其中xxx表示库的名称。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被编译进执行程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的动态库。动态函数库的改变并不影响程序,所以动态函数库的升级比较方便。Linux系统有几个重要的目录存放相应的函数库,如/lib、/usr/lib。2.2.1Linux静态库和动态库

演示代码:message.c:

#include<stdio.h>voidgoodbye_world(void){printf("Goodbye,world!\n");}main.c:

#include<stdlib.h>voidgoodbye_world(void);intmain(intargc,char**argv){goodbye_world();return0;}2.2.2静态库的制作和应用

1.将message.c编译成.o文件[root@vm-devmessage]#gcc-cmessage.c2.由.o文件创立静态库[root@vm-devmessage]#ar-rclibmessage.amessage.o[root@vm-devmessage]#lslibmessage.amain.cmessage.cmessage.o3.在程序中使用静态库[root@vm-devmessage]#gcc-omessage-L.main.c-lmessage[root@vm-devmessage]#lslibmessage.amain.cmessagemessage.cmessage.o[root@vm-devmessage]#./messageGoodbye,world!2.2.3动态库的制作和应用

1.将message.c编译成.o文件[root@vm-devmessage]#gcc-c-fpicmessage.c2.由.o文件创立动态库[root@vm-devmessage]#gcc-shared-olibmessage.somessage.o[root@vm-devmessage]#lslibmessage.somain.cmessage.cmessage.o3.在程序中使用动态库[root@vm-devmessage]#gcc-L.-lmessagemain.c-omessage[root@vm-devmessage]#lslibmessage.somain.cmessagemessage.cmessage.o[root@vm-devmessage]#./message./message:errorwhileloadingsharedlibraries:libmessage.so:cannotopensharedobjectfile:Nosuchfileordirectory[root@vm-devmessage]#cplibmessage.so/usr/lib/[root@vm-devmessage]#./messageGoodbye,world!练习题

2.3Makefile根底和应用Makefile根本结构1

Makefile变量

2Makefile规那么3

make使用

42.3.1Makefile根本结构默认的情况下,make命令会在当前目录下按顺序寻找文件名为“GNUmakefile〞、“makefile〞、“Makefile〞的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile〞这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile〞,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile〞文件名敏感,但是根本上来说,大多数的make都支持“makefile〞和“Makefile〞这两种默认文件名。当然,可以使用别的文件名来书写Makefile,比方:“Make.Linux〞,“Make.Solaris〞,“Make.AIX〞等,如果要指定特定的Makefile,可以使用make的“-f〞和“--file〞参数,如:make-fMake.Linux或make--fileMake.AIX。Makefile的文件名

12.3.1Makefile根本结构Makefile结构和规那么2一个Makefile中通常至少包含如下内容:需要由make工具创立的目标体〔target〕,目标体通常是目标文件、可执行文件或是一个标签;要创立的目标体所依赖的文件〔dependency_file〕;创立每个目标体时需要运行的命令〔command〕。格式为:target:dependency_filescommand要注意的是,command前面是一个“Tab〞键符。使用make的格式为:maketarget这样make就会自动读入Makefile〔也可以是首字母小写makefile〕执行对应target的command语句,并会找到相应的依赖文件2.3.1Makefile根本结构Makefile结构和规那么2Makefile例子:edit:main.okbd.ocommand.odisplay.oinsert.osearch.ofiles.outils.ogcc-oeditmain.okbd.ocommand.odisplay.oinsert.osearch.ofiles.outils.omain.o:main.cdefs.hgcc-cmain.c–omain.okbd.o:kbd.cdefs.hcommand.hgcc-ckbd.c–okbd.ocommand.o:command.cdefs.hcommand.hgcc-ccommand.c–ocommand.odisplay.o:display.cdefs.hbuffer.h

gcc-cdisplay.c –odisplay.oinsert.o:insert.cdefs.hbuffer.hgcc-cinsert.c –oinsert.osearch.o:search.cdefs.hbuffer.hgcc-csearch.c –osearch.ofiles.o:files.cdefs.hbuffer.hcommand.h

gcc-cfiles.c –ofiles.outils.o: utils.cdefs.hgcc–cutils.c –outils.oclean:rmeditmain.okbd.ocommand.odisplay.oinsert.osearch.ofiles.outils.o2.3.1Makefile根本结构make的工作过程

31.make会在当前目录下找名字叫“Makefile〞或“makefile〞的文件。2.如果找到,它会查看该Makefile文件中是否包含了其它的Makefile文件或规那么文件,并读入其它规那么文件。3.在Makefile文件中查找第一个目标文件〔target〕,在上面的例子中,他会找到“edit〞这个文件,并把这个文件作为最终的目标文件。4.如果edit文件不存在,或是edit所依赖的后面的.o文件的文件修改时间要比edit这个文件新,那么,它就会执行后面所定义的命令来生成edit这个文件。5.如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到那么再根据那一个规那么生成.o文件。6.根据.o文件的依赖关系找到相应的.c文件和.h文件,并生成.o文件。7.当所有的.o文件都已经编译好后,make会根据它们最终编译生成可执行文件edit了。练习题

有一个ARM目标机运行的工程,包括以下文件:四个.c源程序如下:#include<stdio.h>externvolatileintbaud;staticintfd;……tty.c:#include<stdio.h>#include<stdlib.h>#include<pthread.h>#defineCMD(0)……keyshell.c:#include<stdio.h>#include<pthread.h>#include"tty.h“#include"gprs.h“……main.c:#include<stdio.h>#include"tty.h“#include"gprs.h“voidgprs_init(){……gprs.c:请为这个工程写一个makefile文件,编译的可执行程序名称是:GPRS。2.3.2Makefile变量

Makefile变量的定义

1〔1〕递归展开方式定义递归展开方式的定义格式为:VAR=var。特点:在引用该变量时进行替换;可以引用其它的之前没有定义的变量;不能在变量后追加内容,否那么,在变量扩展过程中可能导致无穷循环。〔2〕简单扩展型变量简单扩展方式的定义格式为:VAR:=var。特点:在定义处展开,并且只展开一次;不能实现对其后定义变量的引用。2.3.2Makefile变量

Makefile变量的定义

1〔1〕递归展开方式定义CFLAGS:-L.-lmessage–O〔2〕简单扩展型变量CFLAGS:–OCFLAGS=$(LIBDIR)-OLIBDIR=-L.-lmessage2.3.2Makefile变量

Makefile变量的类型

2用户自定义变量预定义变量自动变量环境变量2.3.2Makefile变量

Makefile变量的类型

22.3.2Makefile变量

Makefile变量的类型

22.3.2Makefile变量

“?=〞变量操作符3GNUmake中,还有一个被称为条件赋值的赋值操作符“?=〞。被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。例如:LIBDIR?=stu含义是:如果变量“LIBDIR〞在之前没有定义,就给它赋值“stu〞。否那么不改变它的值。2.3.2Makefile变量

追加变量值

4在Makefile中使用“+=〞〔追加方式〕来实现对一个变量值的追加操作:objects+=another.o这个操作把字符串“another.o〞添加到变量“objects〞原有值的末尾,使用空格和原有值分开。因此可以看到:

objects=main.ofoo.obar.outils.oobjects+=another.o上边的两个操作之后变量“objects〞的值就为:“main.ofoo.obar.outils.oanother.o〞。使用“+=〞操作符,相当于:objects=main.ofoo.obar.outils.oobjects:=$(objects)another.o2.3.2Makefile变量

override指示符5通常在执行make时,如果通过命令行定义了一个变量,那么它将替代在Makefile中出现的同名变量的定义。就是说,对于一个在Makefile中使用常规方式〔使用“=〞、“:=〞或者“define〞〕定义的变量,我们可以在执行make时通过命令行方式重新指定这个变量的值,命令行指定的值将替代出现在Makefile中此变量的值。如果不希望命令行指定的变量值替代在Makefile中的变量定义,那么我们需要在Makefile中使用指示符“override〞来对这个变量进行声明练习题2.3.3Makefile规那么Makefile的规那么包括目标体、依赖文件及其间的命令语句,是make进行处理的依据。Makefile中的一条语句就是一个规那么。在上面的例子中显式地指出了Makefile中的规那么关系,如“$(CC)$(CFLAGS)-c$<-o$@〞,为了简化Makefile的编写,make还定义了隐式规那么和模式规那么。2.3.3Makefile规那么隐规式那么能够告诉make怎样使用传统的技术完成任务,这样,当用户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。make会自动搜索隐式规那么目录来确定如何生成目标文件,如:隐式规那么1OBJS=main.okbd.ocommand.odisplay.oinsert.osearch.ofiles.outils.oCC=gccCFLAGS=-Wall-O-gedit:$(OBJS)$(CC) $^-o $@main.o:main.cdefs.hkbd.o:kbd.cdefs.hcommand.hcommand.o:command.cdefs.hcommand.hdisplay.o:display.cdefs.hbuffer.hinsert.o:insert.cdefs.hbuffer.hsearch.o:search.cdefs.hbuffer.hfiles.o:files.cdefs.hbuffer.hcommand.hutils.o:utils.cdefs.hclean:rmedit$(OBJS)2.3.3Makefile规那么隐式规那么12.3.3Makefile规那么模式规那么2模式规那么用来定义相同处理规那么的多个文件的,模式规那么能引入用户自定义变量,为多个文件建立相同的规那么,简化Makefile的编写。模式规那么中,目标名中需要包含有模式字符“%〞,包含有模式字符“%〞的目标被用来匹配一个文件名,“%〞可以匹配任何非空字符串。例如:〔1〕“%.o:%.c〞,表示:所有的.o文件依赖于对应的.c文件。〔2〕%.o

:

%.c

$(CC)

-c

$(CFLAGS)

$(CPPFLAGS)

$<

-o

$@表示:把所有的“.c“文件都编译成“.o“文件。2.3.4make的使用

2.4linuxgdb调试技术Linuxgdb〔本地〕Gdb远程调试Linuxgdb〔本地〕Linuxgdb简介Gdb主要命令列表Linuxgdb简介GDB是一个功能强大、运行稳定的程序调试工具,通过与gcc的配合使用,为基于linux的软件开发提供了一个完善的调试环境。在使用GDB调试程序之前,可执行程序编译选项应该加上-g-g3包括所有的调试信息。Linuxgdb主要命令列表命令说明命令说明run运行某个程序list显示源程序break设置断点Infolocal显示局部变量watch监视某个变量Infobreak显示断点next单步,函数跳过display显示变量或函数值step单步,函数跳入delete/clear删除断点continue运行到下一断点kill停止运行的程序finish退出函数回到调用quit退出GDB调试实例:源程序//sum.c#include<stdio.h>intCallSum(inta){ intb; intbSum=0; for(b=1;b<=a;b++) { bSum+=b; } returnbSum;}intmain(intargc,char*argv[]){ intMySum; intx; printf("pleaseinputanumber"); scanf("%d",&x); MySum=CallSum(x); printf("\n1+2+3+...+%d=%d\n",x,MySum); return0;}调试实例:编译调试编译:gcc–g3-Wall-osumsum.c调试:

gdbsum//进入gdb调试breakmain//设置断点run//运行程序watchMySum//设置一个监测点s//单步..............Gdb远程调试简介远程调试环境由宿主机GDB和目标机调试GdbServer共同构成,两者通过串口或TCP连接。使用GDB标准程串行协议协同工作,实现对目标机上的系统内核和上层应用的监控和调试功能。GdbServer是嵌入式系统中的一段代码,作为宿主机GDB和目标机调试程序间的一个媒介而存在。Gdb远程调试简介统资源有限性,一般不能直接在目标系统上进行调试,通常采用gdb+gdbserver的方式进行调试。Gdbserver在目标系统中运行,gdb那么在宿主机上运行。gdb+gdbserver是最常用的调试应用程序的方式Gdb远程调试:下载一般linux发行版中都有一个可以运行的gdb,但开发人员不能直接使用该发行版中的gdb来做远程调试,而要获取gdb的源代码包,针对arm平台作一个简单配置,重新编译得到相应gdb。gdb官方网址-gdb下载地址-:///Gdb远程调试:配置编译配置编译gdb配置编译gdbserverGdb远程调试:配置编译gdb#tar-xzvfgdb-6.4-tar-gz#cdgdb-6.4#./configure--target=arm-linux\--prefix=/root/gdb/gdb-install#make

#makeinstall#exportPATH=$PATH:/root/gdb/gdb-install/binGdb远程调试:配置编译gdbserver#cdgdbserver

#./configure--target=arm-linux–host=arm-linux\--prefix=/root/gdbserver/gdb-install#makeCC=arm-linux-gcc

#makeinstall至此,arm-linux-gdb和gdbserver两个工具生成。Gdb远程调试实例

〔1〕编译下载交叉编译sum程序,将sum程序和gdbserver

复制到目标机上。

Gdb远程调试实例

〔2〕启动gdbserver#./gdbserver192.168.0.253:1234sum

宿主机ip端口号Gdb远程调试实例

〔3〕启动arm-linux-gdb#exportPATH=$PATH:/root/gdb/gdb-install/bin#arm-linux-gdbsum(GDB)targetremote192.168.0.105:1234

至此,可以输入gdb命令进行调试了。目标机ip2.5嵌入式Liunx多线程应用程序设计

Linux线程概述1线程根本编程2线程的同步与互斥3线程属性42.5.1Linux多线程概述线程是一种轻量级的进程。与进程最大的不同是线程没有系统资源。线程是操作系统调度的最小单位,可以理解为一个进程是由一个或者多个线程组成的。在操作系统内核中,是按照线程作为调度单位来调度资源的。在一个进程内部,多个线程之间的资源是共享的。也就是说,如果一个进程内部的所有线程拥有相同的代码地址空间和数据空间,那么任意一个线程都可以访问其他线程的数据。多线程中的同步是非常重要的问题。linux线程分为两类:一是核心级线程,二是用户级的线程。用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持。在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创立、调度和撤销等功能,而内核仍然仅对进程进行管理。一般都为用户级的线程。核心级线程允许不同进程中的线程按照同一相对优先调度方法进行调度,这样就可以发挥多处理器的并发优势。采用用户级线程与核心级线程并存的方法。一个用户级线程可以对应一个或几个核心级线程,也就是“一对一〞或“多对一〞模型。这样既可满足多处理机系统的需要,也可以最大限度地减少调度开销。2.5.2线程根本编程线程的创立12.5.2线程根本编程1#include<pthread.h>2#include<stdio.h>3#include<stdlib.h>4void*thread_func(void*arg)//线程函数5{6int*val=arg;7printf("Hi,I'mathread!\n");8if(NULL!=arg){//如果参数不为空,打印参数内容910printf("argumentset:%d\n",*val);11}1213intmain()14{15pthread_ttid;//线程ID16intt_arg=100;//给线程传入的参数值1718if(pthread_create(&tid,NULL,thread_func,&t_arg))//创立线程19perror("Failtocreatethread");2021sleep(1);//睡眠1秒,等待线程执行22printf("Mainthread!\n");2324return0;25}2.5.2线程根本编程

线程的等待2pthread_join()可以用于将当前线程挂起来等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。2.5.2线程根本编程

线程的等待213intmain()14{15pthread_ttid;//线程ID16intt_arg=100;//给线程传入的参数值1718if(pthread_create(&tid,NULL,thread_func,&t_arg))//创立线程19perror("Failtocreatethread");2021sleep(1);//睡眠1秒,等待线程执行22printf("Mainthread!\n");2324return0;25}void*retval;pthread_join(tid,&retval);2.5.2线程根本编程

线程的终止3当线程本身的代码运行结束后,会自动退出线程代码中调用return也会导致线程退出用函数pthread_exit()exit()使调用进程终止pthread_cancel()函数用来取消一个线程的执行2.5.2线程根本编程

线程的终止32.5.2线程根本编程1#include<pthread.h>2#include<stdio.h>3#include<stdlib.h>4void*thread_func(void*arg)//线程函数5{6int*val=arg;7printf("Hi,I'mathread!\n");8if(NULL!=arg){//如果参数不为空,打印参数内容9while(1)10printf("argumentset:%d\n",*val);11}1213intmain()14{15pthread_ttid;//线程ID16intt_arg=100;//给线程传入的参数值1718if(pthread_create(&tid,NULL,thread_func,&t_arg))//创立线程19perror("Failtocreatethread");2021sleep(1);//睡眠1秒,等待线程执行22printf("Mainthread!\n");23pthread_cancel(tid);24return0;25}练习题请写出以下程序的执行结果:#include<pthread.h>#include<stdio.h>#include<stdlib.h>void*thread_func(void*arg)

{int*val=arg;printf("123!\n");

if(NULL!=arg){printf("num=%d\n",*val);}intmain(){pthread_ttid;intsg=20;pthread_create(&tid,NULL,thread_func,&sg)sleep(1);printf("abc!\n");return0;}

2.5.3线程的同步与互斥互斥锁信号量条件变量2.5.3线程的同步与互斥互斥锁〔mutex〕相当于一把锁,可以保证以下三点:原子性:如果一个线程锁定一个互斥锁,那么临界区内的操作要么全部完成,要么一个也不执行。惟一性:如果一个线程锁定一个互斥锁,那么在它解除锁定之前,没有其他线程可以锁定这个互斥锁。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作非繁忙等待:如果一个线程已经锁定一个互斥锁,第二个线程又试图去锁定这个互斥锁,那么第二个线程将被挂起〔不占用任何CPU资源〕,直到第一个线程解除对这个互斥锁的锁定为止。互斥锁12.5.3线程的同步与互斥互斥锁是用一种简单的加锁方法来控制对共享资源的原子操作。这个互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。可以说,这把互斥锁保证让每个线程对共享资源按顺序进行原子操作。互斥锁初始化:pthread_mutex_init(pthread_mutex_t*restrictmutex,constpthread_mutexattr_t*restrictattr)互斥锁上锁:pthread_mutex_lock(pthread_mutex_t*mutex)互斥锁判断上锁:pthread_mutex_trylock(pthread_mutex_t*mutex)互斥锁解锁:pthread_mutex_unlock(pthread_mutex_t*mutex)消除互斥锁:pthread_mutex_destroy(pthread_mutex_t*mutex)2.5.3线程的同步与互斥P原语操作的动作是:

〔1〕sem减1;

〔2〕假设sem减1后仍大于或等于零,那么进程继续执行;

〔3〕假设sem减1后小于零,那么该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。

V原语操作的动作是:

〔1〕sem加1;

〔2〕假设相加结果大于零,那么进程继续执行;

〔3〕假设相加结果小于或等于零,那么从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。信号量22.5.3线程的同步与互斥sem_init():用于创立一个信号量,并初始化它的值。sem_wait()和sem_trywait()都相当于P操作,在信号量大于零时它们都能将信号量的值减一,两者的区别在于假设信号量小于零时,sem_wait()将会阻塞进程,而sem_trywait()那么会立即返回。sem_post()相当于V操作,它将信号量的值加一同时发出信号来唤醒等待的进程。sem_getvalue()用于得到信号量的值。sem_destroy()用于删除信号量。信号量22.5.3线程的同步与互斥条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的缺乏,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。条件变量是一种可以使线程〔不消耗CPU〕等待某些事件发生的机制。某些线程可能守候着一个条件变量,直到某个其他的线程给这个条件变量发送一个信号,这时这些线程中的一个线程就会苏醒,处理这个事件。条件变量32.5.3线程的同步与互斥〔1〕条件变量初始化函数intpthread_cond_init(pthread_cond_t*cond,__constpthread_condattr_t*cond_attr)〔2〕销毁条件变量函数intpthread_cond_destroy(pthread_cond_t*__cond)〔3〕唤醒线程等待条件变量函数intpthread_cond_signal(pthread_cond_t*__cond)〔4〕等待条件变量〔阻塞〕函数intpthread_cond_wait(pthread_cond_t*__restrict__cond,pthread_mutex_t*__restrict__mutex)〔5〕在指定的时间到达前等待条件变量函数intpthread_cond_timedwait(pthread_cond_t*__restrict__cond,pthread_mutex_t*__restrict__mutex,__conststructtimespec*__restrict__abstime)2.5.4线程属性绑定属性别离属性属性的设置生产者消费者多线程设计实验实验〔1〕生产者消费者多线程设计实验实验〔2〕生产者消费者多线程设计实验实验〔3〕生产者写入共享的循环缓冲区函数PUT:voidput(structprodcons*b,intdata){pthread_mutex_lock(&b->lock);//获取互斥锁while((b->writepos+1)%BUFFER_SIZE==b->readpos){//如果满pthread_cond_wait(&b->notfull,&b->lock);//等待状态变量b->notfull,不满那么跳出阻塞。}b->buffer[b->writepos]=data;//写入数据b->writepos++;if(b->writepos>=BUFFER_SIZE)b->writepos=0;pthread_cond_signal(&b->notempty);//设置状态变量pthread_mutex_unlock(&b->lock);//释放互斥锁}生产者消费者多线程设计实验实验〔4〕消费者读取共享的循环缓冲区函数GET

:intget(structprodcons*b){intdata;pthread_mutex_lock(&b->lock);//获取互斥锁while(b->writepos==b->readpos){//如果读写位置相同pthread_cond_wait(&b->notempty,&b->lock);//等待状态变量b->notempty,不空那么跳出阻塞。否那么无数据可读。}data=b->buffer[b->readpos];//读取数据b->readpos++;if(b->readpos>=BUFFER_SIZE)b->readpos=0;pthread_cond_signal(&b->notfull);//设置状态变量pthread_mutex_unlock(&b->lock);//释放互斥锁returndata;}2.6嵌入式Linux下C和汇编的混合编程

混合编程概述

1C调用汇编

2汇编调用C3C内嵌汇编42.6.1混合编程概述

C和汇编的混合编程及类型

1C语言是被称为高级的低级语言,原因是在C语言中,有许多针对硬件的操作,能很好地利用硬件特性。从一方面来说,C语言也是高级语言,它能很方便地实现各种复杂的编程算法。在嵌入式系统编程中,C语言是首选的程序设计语言,但在某些特定条件下,C语言无法精确地操作硬件,此时往往采用嵌入或调用汇编程序的方法来解决此类问题。这就是混合编程。混合编程从方式上主要包括三类,即:C程序调用汇编程序汇编程序调用C程序C程序内嵌汇编2.6.1混合编程概述

ATPCS标准简介2基于ARM的嵌入式C和汇编的混合编程需要遵循一定的标准,这就是过程调用标准ATPCS标准。ATPCS规定了子程序间相互调用的根本规那么,调用过程中存放器的使用规那么、数据栈的使用规那么及参数的传递规那么。2007年,ARM公司推出了新的过程调用标准AAPCS〔ARMArchitectureProduceCallStandard〕,它只是改进了原有的ATPCS的二进制代码的兼容性。〔1〕存放器使用规那么子程序间通过存放器R0~R3传递参数,存放器R0~R3可记作A1~A4。被调用的子程序在返回前无须恢复存放器R0~R3的内容。在子程序中,ARM状态下使用存放器R4~R11来保存局部变量,存放器R4~R11可记作V1~V8;Thumb状态下只能使用R4~R7来保存局部变量。存放器R12用作子程序间调用时临时保存栈指针,函数返回时使用该存放器进行出栈,记作IP;在子程序间的链接代码中常有这种使用规那么。通用存放器R13用作数据栈指针,记作SP。通用存放器R14用作链接存放器。通用存放器R15用作程序计数器,记作PC。2.6.1混合编程概述

ATPCS标准简介2〔2〕数据栈使用规那么过程调用标准规定数据栈为FD〔Fulldescending满递减堆栈〕类型,并且对数据栈的操作时要求8字节对齐的。〔3〕参数传递规那么对于参数个数可变的子程序,当参数个数不超过4个时,可以使用存放器R0~R3来传递;当参数个数超过4个时,还可以使用数据栈进行参数传递。2.6.1混合编程概述

ATPCS标准简介2〔4〕子程序结果返回规那么结果为一个32位的整数时,通过存放器R0返回;结果为一个64位整数时,通过存放器R0,R1返回。结果为一个浮点数时,可以通过浮点运算部件的存放器F0、D0或者S0来返回;结果为复合型的浮点数〔如复数〕时,可以通过存放器F0~Fn或者D0~Dn来返回。对于位数更多的结果,需要通过内存来传递。2.6.1混合编程概述

ARM开发工具编译环境和GNU编译环境的差异32.6.2C调用汇编

在C中调用汇编文件中的函数要点:在C中声明函数原型,并加extern关键字;在汇编中用global导出函数名,并用该函数名作为汇编代码段的标识,最后用movpc,lr返回。从C的角度,并不知道该函数的实现是用C还是汇编。更深的原因是因为C的函数名起到说明函数代码起始地址的作用,这个和汇编的label是一致的。2.6.2C调用汇编

//main.c#include<stdio.h>intgVar_1=12;externasmDouble(void);

intmain(){printf("originalvalueofgVar_1is:%d",gVar_1);asmDouble();printf("modifiedvalueofgVar_1is:%d",gVar_1);return0;}//Asmfile_gnu.s:

.text.globalasmDouble.externgVar_1asmDouble:ldrr0,=gVar_1ldrr1,[r0]movr2,#2mulr3,r1,r2strr3,[r0]movpc,lr.end以上程序通过使用全局变量gVar_1实现汇编和C之间数据的传递,这是最简单的调用方式了。

2.6.2C调用汇编

//main.c#include<stdio.h>externintasmMulti(int,int);

intmain(){intx=5;inty=6;intz;printf("\nx=%d,y=%d",x,y);z=asmMulti(x,y);printf("\nx*y=%d\n",z);return0;}

//Asmfile_gnu.s:

.text.globalasmMulti

asmMulti:mulr2,r0,r1movr0,r2movpc,lr

.end

对以上程序加以修改,以实现汇编和C之间参数的传递,如下:2.6.3汇编调用C

//cFile.c

intcFun(inta,intb,intc){returna+b+c;}

//Asmfile_gnu.s:

.text.externcFunEntry:movr0,#11movr1,#22movr2,#33BLcFun.end

在汇编中调用C的函数,需要在汇编中使用extern声明对应的C函数名,然后将C的代码放在一个独立的C文件中进行编译,剩下的工作由连接器来处理。2.6.4C内嵌汇编

__asm(“指令;\指令;\…\指令;〞);GNUARM环境下内嵌汇编语法最简单的格式如下:1voidmain(void)2{3intx=0xAA;4__asm5(6“MOVR5,#0xAA;/*不能直接引用C代码中定义的变量x*/7MOVR6,#0xBB;8CMPR1,#0;〞9);10while(1);11}例子:2.6.4C内嵌汇编

__asm____volatile__("asmstatements":outputs:inputs:registers-modified);

为了实现复杂的功能,完整的内联汇编代码格式定义如下:__asm__(("mov%0,%1,ror#1":"=r"(result):"r"(value)));例子:其中,__asm__表示汇编代码的开始,其后可以跟__volatile__〔这是可选项〕,其含义是防止“asm〞指令在编译过程中因代码优化而被删除、移动或组合;然后就是小括弧,括弧内的内容总共由四局部组成:汇编语言模块,输出局部,输入局部,限制约束局部,各局部使用“:〞分隔。2.6.4C内嵌汇编

汇编语句模板由汇编语句序列组成,语句之间使用“;〞、“\n〞、“\n\t〞分开,它可以为空。当在汇编语句模板有多条指令时,可以在一对引号中列出全部指令,也可将一条或几条指令放在一个引号中,所有指令放在多对引号中。在C中内联汇编,需要处理的一个问题就是如何与C语句中的变量相结合、传入汇编代码、从汇编代码中传出改变的值。在内联汇编代码的指令部中,使用占位符来表示需要使用存放器的操作数,有几个占位符就表示有几个变量需要和存放器相结合,汇编器在编译和连接的时候就会相应的根据后面的约束条件进行处理。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,…%9。指令中使用占位符表示的操作数,总被视为long型〔4个字节〕,但对其施加的操作根据指令可以是字或者字节,当把操作数当做字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节,可在%和序号之间插入一个字母,“b〞代表低字节,“h〞代表高字节,例如:%h1。asmstatements〔汇编语言模块〕12.6.4C内嵌汇编

汇编语句模板由汇编语句序列组成,语句之间使用“;〞、“\n〞、“\n\t〞分开,它可以为空。当在汇编语句模板有多条指令时,可以在一对引号中列出全部指令,也可将一条或几条指令放在一个引号中,所有指令放在多对引号中。在C中内联汇编,需要处理的一个问题就是如何与C语句中的变量相结合、传入汇编代码、从汇编代码中传出改变的值。在内联汇编代码的指令部中,使用占位符来表示需要使用存放器的操作数,有几个占位符就表示有几个变量需要和存放器相结合,汇编器在编译和连接的时候就会相应的根据后面的约束条件进行处理。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,…%9。指令中使用占位符表示的操作数,总被视为long型〔4个字节〕,但对其施加的操作根据指令可以是字或者字节,当把操作数当做字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节,可在%和序号之间插入一个字母,“b〞代表低字节,“h〞代表高字节,例如:%h1。outputs〔输出局部〕22.6.4C内嵌汇编

输入局部与输出局部相似,但没有“=〞。这局部指明程序的输入参数,每个输入参数都括在一对圆括号内,各参数用逗号分开。每个参数前加一个用双引号括起来的标志,告诉编译器把该参数装入到何处。inputs〔输入局部〕3告诉编译器程序中将要修改的存放器。每个存放器都用双引号括起来,并用逗号隔开。如“r12〞。如果汇编程序中引用了某个特定的硬件存放器,就应该在此处列出这些存放器,以告诉编译器这些存放器的值被改变了。如果汇编程序中用某种不可预测的方式修改了内存,应该在此处加上“memory〞。这样以来,在整个汇编程序中,编译器就不会把它的值缓存在存放器中了。限制约束局部42.6.4C内嵌汇编

注意,汇编语言模块为必选项,而输入局部、输出局部及限制约束局部为可选项,当输入局部存在,而输出局部不存在时,冒号“:〞要保存,当限制约束局部存在时,三个冒号都要保存。以上内嵌汇编代码中操作数的引用使用的是%后面跟一个数字,%0代表第一个操作数,%1代码第二个操作数,往后的类推。这个方法不便于维护代码,如果想插入一个操作数,那么就不得不从新修改操作数编号。因而,还有一种表达格式,它用符号来代替数字,比方:__asm__("mov%[result],%[value],ror#1":[result]"=r"(y):[value]"r"(x));

__asm__(("mov%0,%1,ror#1":"=r"(result):"r"(value)));等价于:练习题写出程序的执行结果。2.7嵌入式Linuxsocket网络编程根底

socket的概念

1socket编程根底2

socketAPI及编程流程32.7.1socket简介socket接口是TCP/IP网络的API,socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。socket的引入是为了方便联网应用程序的编程,在UNIX的BSD版本第一次使用了这种接口,因此他也叫做BSD套接字。应用程序可以使用这种统一接口收发网络上的数据,网络的socket数据传输是一种特殊的I/O口,socket也是一种文件描述符。套接口是IP地址与端口号的组合,在tcp和udp协议中用来标识一个连接,网络应用程序之间通过套接口来实现通信。

2.7.1socket简介套接口支持很多协议,典型的是TCP和UDP协议,通信程序采用传统的效劳器/客户端结构。2.7.1socket简介socket类型:1.流式套接字〔SOCK_STREAM〕流式套接字提供可靠的、面向连接的信息流;针对于面向连接的TCP效劳应用,它使用TCP协议,从而保证了数据传输的可靠性和顺序性。2.数据报套接字〔SOCK_DGRAM〕数据报套接字定义了一种无可靠、面向无连接的效劳,对应于无连接的UDP效劳应用,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无过失的。它使用数据报协议UDP。3.原始套接字〔SOCK_RAW〕原始套接字允许对底层协议如IP或ICMP进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。2.7.2socket编程根底

socket的数据结构

1structsockaddr{unsignedshortsa_family;/*地址族,AF_xxx*/charsa_data[14];/*14 字节的协议地址,包括IP地址和端口号*/};

structsockaddr_in{shortintsa_family;/*地址族*/unsignedshortintsin_

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论