1.03k likes | 1.33k Views
第 6 章 嵌入式 Linux 应用程序开发. 本章主要内容. Linux 介绍 Linux 开发工具 Linux 常用命令 Linux 编程. 6.1 Linux 介绍. Linux 定义 Linux 优势 Linux 种类 如何选择 linux 版本. 1. Linux 定义.
E N D
本章主要内容 • Linux介绍 • Linux开发工具 • Linux常用命令 • Linux编程
6.1 Linux介绍 • Linux定义 • Linux优势 • Linux种类 • 如何选择linux版本
1. Linux定义 • Linux一般是指Linux内核、Linux系统、Li nux发行版。严格意义上说Linux是指由Linus Torvalds维护的并发布的内核。它的代码基只包括内核而不包括其它方面的应用。内核提供系统核心服务,如进程管理,进程的调度,虚拟文件系统,内存的管理等等。 • 嵌入式Linux,一般是指把Linux内核移植到一个专用嵌入式设备的CPU和主板上。现在有很多公司提供嵌入式Linux解决方案。通常包括一个移植的内核和嵌入式Linux的开发工具以及根据应用需要裁减的应用程序,有时还提供实时扩展的内核。就大部分而言,嵌入式Linux和桌面Linux提供的API函数和内核源代码都是相同的。开发工具主要包括源码浏览器、交叉编译器、调试器、项目管理软件等等。这些工具一般都是装在主机上。
2. 嵌入式Linux种类 • 嵌入式系统的划分一般是根据使用对象进行划分,如消费类电子等等。为了更好的理解所设计的系统,这里按照尺寸,时间约束,网络,用户交互的程度划分。 • 尺寸:小系统,中等系统,大系统 小系统-CPU的性能比较低,ROM<2MB RAM<4MB 中系统-CPU的性能中等,ROM32MB RAM64MB左右 大系统-CPU性能高,存储量大 • 时间约束:实时,非实时 • 网络连接性 • 用户交互
3. Linux优势 • Linux是免费的 • Linux的费用低 • Linux的所有部分可以充分地定制 • Linux可以运行在低档,便宜的硬件平台 • Linux是强大的 • Linux对源代码质量有一个高标准 • Linux内核非常小,而且紧凑 • Linux与很多常见的操作系统高度兼容 • Linux有很好的支持 • Linux有很多合适的工具
4. 如何选择Linux的版本 • DIY-自己动手做一个满足需要的Linux版本 • 选择商用Linux版本 • 授权和涉及到的授权 • 是否满足你的需要,是否有合适的开发工具 • 是否能够提供足够的支持 • 信誉 • 文档
6.2 Linux开发工具 • GCC-GNU Compiler Collection,包含一系列针对不同语言和不同目标平台,以及可运行在不同主机系统上的编译器 • Make-维护文件的依赖关系 • GDB-负责程序调试 • Glibc-标准C库,数学库,以及板级程序库 • 集成开发环境
1. GCC-程序编译 • GCC支持的前端 GCC支持的语言包括C(gcc),C++(g++), Fortran(g77), Java(GCJ)和Ada(GNAT) • GCC支持的后端 后端的扩展机制使得GCC能够支持多种类型的体系结构,从RISC,CISC到DSP都有支持。 • GCC支持的主机系统 它可以运行在几乎所有操作系统的平台上(GNU/Linux)DOS Windows
GCC程序编译 • hello.c #include <stdio.h>int main(void){printf ("Hello world, Linux programming!\n");return 0;}然后执行下面的命令编译和运行这段程序: # gcc hello.c -o hello# ./helloHello world, Linux programming!
GCC选项 • -g:在可执行文件中输出调试信息,通常是为调试器gdb所用 • -On:代码优化,n随着系统的不同而不同 • -Idir:指定头文件搜索目录 gcc foo.c –I/home/chen/include –o foo • -Ldir:指定在目录中需要搜索的库(动态库.so 和静态库.a) gcc foo.c –L/home/chen/lib –lfoo –o foo • -static:链接时使用静态链接 • -elf2flt:将可执行文件头转换为flt平板格式 • -c:只预处理,编译和汇编生产obj文件
2. Make-维护文件的依赖关系 • make:使应用程序的编译和连接自动化 • make:使产生可执行文件的时间大为缩短 • Make:可以管理大型项目 • make:按照代码之间的时间依赖关系维护文件 • make规则文件:Makefile,该文件包括目标定义,执行命令,宏定义和make伪指令 • Makefile文件的编写可以手工,也可以自动,使用工具软件可以自动生成Makefile文件 • Make使用的缺省文件为当前目录下的makefile或Makefile,也可以使用命令行参数-f指定文件名 • $make –f newmakefile
Makefile文件 • Makefile文件包含以下内容 • 宏定义 • 源文件之间的相互依赖关系 • 可执行的命令 • Makefile中的宏 宏名称=设定值 如source =test1.c test2.c test3.c • 当要使用宏时,需要用()将宏扩起来 • 依赖关系-两个或多个文件间彼此的关系 目标:依赖文件;命令 如 test:test.c; gcc –o test test.c test和test.c有依赖关系,如果test.c改变,test也随之改变 • 或是把[命令]写在下一行,需有定位字元作为前导,因此可写成 • test:test.c gcc-o test test.c
Makefile文件 • 01 # Makefile for cshow 02 # By Ivor Chen 03 # 08/31/1994 04 CC = gcc 05 OPTIMIZE = -fomit-frame-pointer -O2 -s 06 CFLAGS = $(DEFINES) $(OPTIMIZE) 07 LFLAGS = -N 08 PROGS = cshow 09 PROGS_O = cshow.o 10 LIBS = -lvgagl -lvga 11 all: progs 12 progs: $(PROGS) 13 objs: $(PROGS_O) 14 $(CC) $(CFLAGS) -c -o $*.o $.depend 15 gcc -MM $(patsubst %.o,%.c,$(PROGS_O)) >>.depend 1617 include .depend • 18 clean: • rm *.o
Make指令 • Make [flags] [macro definition] [targets] • flags • -f file 指定file为描述文件 • -I dir 当包含其它makefile文件时,利用该选项搜索 • -h help文档 • marco definition • make “LIBS = -ll -ls” • target指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。 • make clean
3. GDB-程序的调试 • GDB是自由软件基金会(Free Software Foundation,FSF)的软件工具之一。它的作用是协助程序员找到代码中的错误。如果没有GDB的帮助,程序员要想跟踪代码的执行流程,唯一的办法就是添加大量的语句来产生特定的输出。但这一手段本身就可能会引入新的错误,从而也就无法对那些导致程序崩溃的错误代码进行分析。GDB的出现减轻了开发人员的负担,可以在程序运行的时候单步跟踪自己的代码,或者通过断点暂时中止程序的执行。此外,他们还能够随时察看变量和内存的当前状态,并监视关键的数据结构是如何影响代码运行的。
GDB调试方法 • 如果想对程序进行调试,必须先在用GCC编译源代码时加上-g选项,以便产生GDB所需要的调试符号信息。例如,debugme.c是一个存在错误程序,可以使用如下的命令对其进行编译,同时产生调试符号:# gcc -g debugme.c -o debugme • 如果愿意的话,还可以在编译时使用“-ggdb”选项来生成更多的调试信息。由于这些调试信息中的相当一部分是GDB所特有的,所以生成的代码将无法在其它调试器中正常调试。对于大多数情况来说,普通的-g选项就足够了。需要注意的是,GCC虽然允许同时使用-g(调试)和-o(优化)选项,但优化会影响最终生成的代码,导致程序源代码和二进制代码之间的关系变得复杂起来。如果不想为调试制造障碍,建议不要将-g和-o选项一同使用,并且只在程序彻底调试完后才开始进行代码优化。这样调试过程将变得相对轻松和愉快。
3. GDB-程序的调试 • GDB具有远程调试功能-对嵌入式非常有用 • GDB可以调试各种程序,包括C,C++,JAVA,PASCAL等 • 远程调试可以设置断点,检验内存,同目标交换信息-相当于简易的仿真器 • GDB可以显示其自身和所调试的目标间的远程串行调试信息,也可依将该信息记录到日志文件中去。 • GDB可以求解在控制台中输入的任意C表达式的值,包括包含有远端目标的函数功能调用的表达式 • print fo(sh_sci[current_sci]->smr.brg) • GDB拥有脚本语言,允许对目标自动的设置和检测。 • GDB拥有跟踪点的功能,该功能可以记录某个运行程序的信息,而尽可能的不打断程序收集数据
GDB-程序的调试 • Host > gdb myprogram • Gdb>target remote /dev/ttys0 • Gdb>load //程序运行至main • Gdb >display foo //目标在main()处停止 • gdb> stepi //目标执行一个指令 • Gdb>quit //退出调试
4. 集成开发环境 • 很多公司提供集成开发环境,一般这些集成开发环境 主要用于本地的开发,也可以通过定制为交叉开发环境。下表列出了几种集成开发环境
集成开发环境 • 在Linux环境下kdevelo和SourceNavigator比较流行 • 在Windows环境下sourceInsight比较流行 • 最重要是个人的嗜好,偏好于哪个编程环境,可以是图形的,也可以是命令行的。
5. Glibc • 由于嵌入式系统的尺寸决定了GNU lib有时不太合适,有很多库可以选择,这里给出了uclibc,diet libc,yaffs • uclibc • uclibc是开发嵌入式Linux系统的c库,它比glibc库小,但是基本上glibc支持的应用uclibc都支持,把应用从glibc移植到uclibc只是重新编译源代码就可以了,uclibc库甚至支持共享库和线程。 • 具体请参看 http://www.uclibc.org • diet libc • diet libc和glibc兼容,它和uclibc不同的是它从头写一个嵌入式库。
6.3 Linux编程 • Hello world • 目录和文件 • 标准IO库 • 进程和线程 • 进程的建立和终止 • 进程间通信 • 多线程编程
Linux编程-hello world • #include <stdio.h> • int main(void) { • printf(“hello world”); • return 0; • } • gcc –o hello.c • ./a.out • $hello world
Linux编程 -目录和文件 • 文件和目录 • 文件属性 • 文件类型 • 文件操作
Linux编程 -目录和文件 • Linux以一种层次的树状结构来管理文件的名字空间。“树”由文件和目录组成,其中所有文件都在“叶子”的位置 • 目录的内容是所有在该目录底下的文件及其它子目录的名字信息 • 文件和目录的名字是由一些除去“/”和空字符的ASCII字符组成 • 根目录通常写为“/” • 文件名字在整个文件系统中不一定唯一,但要求在该文件所在的目录中唯一即可
Linux文件结构 • /根目录 ┃ ┏━━━━┳━━━━━┳━━━━━┳━━━━━╋━━━━━┳━━━━━┳━━━━━┳━━━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ bin home dev etc lib sbin tmp usr var ┃ ┃ ┏━┻━┓ ┏━━━━┳━━━┳━━━┳━┻━┳━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ rc.d cron.d X11R6 src lib local man bin ┃ ┃ ┃ ┏━━━┳━━━┳━┻━┳━━━━┓ ┃ ┏━━━╋━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ init.d rc0.d rc1.d rc2.d …… linux bin lib src • linux采用的是树型结构。最上层是根目录,其他的所有目录都是从根目录出发而生成的。微软的DOS和windows也是采用树型结构,但是在DOS和windows中这样的树型结构的根是磁盘分区的盘符,有几个分区就有几个树型结构,他们之间的关系是并列的。但是在linux中,无论操作系统管理几个磁盘分区,这样的目录树只有一个。从结构上讲,各个磁盘分区上的树型目录不一定是并列的。
文件结构 • /bin 二进制可执行命令/dev 设备特殊文件/etc 系统管理和配置文件/etc/rc.d 启动的配置文件和脚本/home 用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示/lib 标准程序设计库,又叫动态链接共享库,作用类似windows里的.dll文件/sbin 系统管理命令,这里存放的是系统管理员使用的管理程序/tmp 公用的临时文件存储点/root 系统管理员的主目录(呵呵,特权阶级)/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。/lost+found 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里/proc 虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。
文件和目录-文件属性 • 文件类型 • 文件长度 • 文件所有者 • 文件的许可权-读,写,执行 • 文件最后修改时间
Linux编程 -文件类型 • 普通文件 这是种最常见的文件,这种文件包含了某种形式的数据,对普通文件的解释由处理该文件的应用程序进行:如 xxx.c lilo.conf • 目录文件 保护了其它文件的名字以及指向与这些文件有关信息的指针。对一个目录文件具有读许可权的任一进程都可以读该目录的内容,但只有内核可以写目录文件 • 字符特殊文件 用于系统中某些类型的设备 • 块特殊文件 典型用于磁盘管理,系统中的所有设备或者是字符特殊文件,或者是块文件 • FIFO(命名管道) 主要用于进程间通信 • 套接口(socket) 主要用于网络通信 • 符号连接 一个文件指向另一个文件
linux编程-文件属性 位置1 2 3 4 5 6 7 文件属性 文件数 拥有者 所属的group文件大小 建档日期 文件名 drwx------ 2 Guest users 1024 Nov 21 21:05 Mail -rwx--x--x 1 root root 89080 Nov 7 22:41 tar* -rwxr-xr-x 1 root bin 5013 Aug 15 9:32 uname* lrwxrwxrwx 1 root root 4 Nov 24 19:30 zcat->gzip -rwxr-xr-x 1 root bin 308364 Nov 29 7:43 zsh* -rwsr-x--- 1 root bin 9853 Aug 15 5:46 su*
文件和目录-文件操作 • cd命令 • mkdir,rmdir命令 • cp命令 • 具体用法是: • cp –r 源文件(source)目的文件(target).参数r是指连同元文件中的子目录一同拷贝 • rm命令 • rm –r 目录名 • 这个操作可以连同这个目录下面的子目录都删除,功能上和rmdir相似。 • rm –f 文件名(目录名) • 这个操作可以进行强制删除。
Linux编程-标准IO库 • 标准IO库和DOS/windows基本相同,这里就不在赘述了
Linux编程-进程和线程 • 进程:普通的解释是,进程是程序的一次执行 • 线程:线程可理解为进程中执行的一段程序片断。 • 区别 • 进程间是独立的,这表现在内存空间,上下文环境,而线程运行在进程空间内。一般来讲(不使用特殊技术)进程是无法突破进程边界存取其他进程内的存储空间,而线程由于处于进程空间内,所以同一进程所产生的线程共享同一内存空间。 • 同一进程中的两段代码不能够同时执行,除非引入线程。线程是属于进程的,当进程退出时,该进程所产生的线程都会被强制退出并清除。线程占用的资源要少于进程所占用的资源。 • 进程和线程都可以有优先级。
Linux编程-进程 • 多进程编程的优点 • Linux下进程的结构 • Linux下进程的控制 • Linux下进程间通信
Linux编程-进程 • Linux多进程编程的优点 • 并行化 • 简单有序 • 互补干扰 • 事务化
堆栈 数据段 代码段 Linux编程-进程的结构 • Linux下一个进程在内存里有三部分的数据:代码段,堆栈段,数据段 • 代码段:存放程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段 • 堆栈段:存放子程序的返回地址,子程序参数以及程序的局部变量 • 数据段:存放程序的全局变量,常数以及动态数据分配的数据空间
Linux编程-进程控制 • 进程标示 ID-每个进程都有一个非负整型的唯一进程ID。因为进程ID表示符是唯一的,常将其用做其它标识符的一部分,以保证唯一性。 • #include <sys/types.h> • #include<unistd.h> • pid_t getpid(void); //返回:调用进程的进程ID • pid_t getppid(void); //返回:调用进程的父进程ID
Linux编程-进程控制 • fork()函数和vfork()函数 • fork() vfork:创建一个新的进程 • 语法:pid_t fork(); pid_t vfork(); • 说明:本系统调用产生一个新的进程, 叫子进程, 是调用进程的一个复制品. 调用进程叫父进程, 子进程继承了父进程的几乎所有的属性 。但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。它们再要交互信息时,只有通过进程间通信来实现 • void main(){ int i; if ( fork() == 0 ) { /* 子进程程序 */ for ( i = 1; i <1000; i ++ ) printf("This is child process\n"); } else { /* 父进程程序*/ for ( i = 1; i <1000; i ++ ) printf("This is process process\n"); } • }
Linux编程-进程控制 • exec函数族 • 当进程调用一种exec函数时,该进程完全由新程序代换, • 而新程序则从其main()函数开始执行。 • 进程终止 • 正常终止:从main返回,调用exit, _exit • 异常终止:调用abort,由一个信号终止
Linux编程-进程间通信 • 管道和FIFO • 信号量 • 消息队列 • 共享内存
进程间通信-管道 • 管道是进程之间单向的数据流:一个进程写入管道的所有数据都有内核定向到另一个进程,另一个进程由此可以从管道中读取数据 进程 内核
进程间通信-管道 • 管道被看作是打开的文件,但是在已经装载的文件系统中没有映象 • 管道是单向的,只提供一个方向的数据流,如果需要双向数据流,必须创建两个管道,每个方向一个 • 一个管道可以被多个进程使用,如果一个管道被多个进程使用,必须使用文件加锁机制
Linux通信-管道的创建和撤销 • 创建管道 • int pipe(int fd[2]); • 该函数返回两个文件描述字:fd[0],fd[1]。前者打开文件读,后者打开文件写。 • 由一个进程创建一个管道后,调用fork派生一个自身的拷贝。 • 从管道中读数据 • read(int fd, void *buff, unsigned nbytes) • 管道描述符fd, 传送数据地址buf,传送字节数nbytes • 向管道中写数据 • write(int fd, void *buff, unsigned nbytes) • popen 建立一个管道 • pclose 删除一个管道
进程间通信-管道 • int main (void) { • int n, fd [2]; • pid_t pid; • char line [MAXLINE]; • if (pipe (fd) < 0) • err_sys ("pipe error"); • if ( (pid = fork ()) < 0) • err_sys ("fork error"); • else if (pid > 0) { // parent • close (fd [0]); • write (fd [1], "hello world\n", 12); • } else { // child • close (fd [1]); • n = read (fd[0], line, MAXLINE); • write (STDOUT_FILENO, line, n); • } • exit (0); • }
进程间通信-管道 管道固有的缺陷 • 因为读数据的同时也将数据从管道移去,因此不能用来对多个接受者广播数据 • 管道中的数据被当作字节流,因此无法识别信息的边界。如果写进程发送不同长度的数据对象通过管道,那么读进程不能确定发送了多少个对象,或是它不能确定对象的边界 • 如果一个管道有多个读进程,那么写进程不能发送数据到指定的读进程。同样,如果有多个写进程,那么没有办法来判别是它们中的哪一个发送数据。
进程间通信-FIFO • 管道的一个主要缺点是:用户无法打开一个现有的管道,除非管道是由一个公共的祖先进程创建的,否则两个任意进程就不能共享同一个管道 • FIFO命名管道,指代先进先出。每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO • FIFO是半双工的
进程间通信-FIFO • 创建FIFO • mknod():创建一个命名管道 • 打开FIFO • open():打开命名管道 • 读FIFO • read():从FIFO中读数据 • 写FIFO • write():向FIFO中写数据 • 关闭FIFO • Close():关闭FIFO • 删除FIFO • unlike()减少文件的一个连接数,如果连接数为0,则删除 • remove()删除文件