1k likes | 1.22k Views
嵌入式系统 第十一章 嵌入式 Linux 系统的应用程序开发. 第 十一 章 Agenda. 11.1 Linux 应用程序开发概述 11.2 Vi 的使用 11.3 使用 gcc 进行 Linux 应用程序开发 11.4 GNU 的 make 和 makefile 11.5 嵌入式 Linux 的 GUI. 11.1 Linux 应用程序开发概述. 嵌入式 Linux 的应用程序开发基本等同于 Linux 的应用程序开发,除了采用的是交叉编译环境,采用特定于嵌入式系统的开发库(如 ulibc 库,或者特定的 GUI 库等).
E N D
嵌入式系统 第十一章 嵌入式Linux系统的应用程序开发
第十一章 Agenda 11.1 Linux 应用程序开发概述 11.2 Vi 的使用 11.3 使用gcc 进行Linux 应用程序开发 11.4 GNU 的make 和makefile 11.5 嵌入式Linux 的GUI
11.1 Linux 应用程序开发概述 • 嵌入式Linux的应用程序开发基本等同于Linux的应用程序开发,除了采用的是交叉编译环境,采用特定于嵌入式系统的开发库(如ulibc库,或者特定的GUI库等)
嵌入式Linux 应用程序开发 • Linux下的应用程序主要有两种特殊的文件: • 可执行文件是能够被计算机直接执行的程序,相当于win平台下exe文件。(本章讨论,一般是elf格式) • 脚本程序则是一组指令,这些指令将由另外一个程序(一般来说是shell解释器)来执行,相当于dos下的bat文件。(略)
嵌入式Linux应用程序开发 • 将被搜索的那些子目录都被保存到一个名字为PATH的shell变量里面,和dos下的情况差不多。PATH是由系统管理员预先配置好了的,通常包括 /bin,/usr/bin,/usr/local/bin • 你可以使用echo $PATH来显示。如果你是使用的root登陆系统,则还会多包括以下目录: /sbin,/usr/sbin。
嵌入式Linux应用程序开发 • 可以使用linux操作系统+vi/vim编辑器或者emacs编辑器+gcc编译和链接器 (我们采用的是这个最原始和有效的方法) • 可以使用windows操作系统+cgywin编译环境 • 还可以使用linux操作系统+kdeveloper和Kylix等IDE环境
第十一章 Agenda 11.1 Linux 应用程序开发概述 11.2 Vi 的使用 11.3 使用gcc 进行Linux 应用程序开发 11.4 GNU 的make 和makefile 11.5 嵌入式Linux 的GUI
Vi • Vi 是Linux 系统的第一个全屏幕交互式编辑程序,它从诞生至今一直得到广大用户的青睐,历经数十年仍然是人们主要使用的文本编辑工具,足以见其生命力之强,而强大的生命力是其强大的功能带来的。 • 目前,安装Linux之后,一般采用的是vim,它支持c 关键字的不同颜色的显示
Vi 的3种模式 Vi 有3 种模式,分别为命令行模式、插入模式及底行模式: • (1)命令行模式 • 用户在用Vi 编辑文件时,最初进入的为命令行模式。在该模式中可以通过上下移动光标进行删除字符”或“整行删除”等操作,也可以进行“复制”、“粘贴”等操作,但无法编辑文字。 • (2)插入模式 • 只有在该模式下,用户才能进行文字编辑输入,用户可以按[ESC]键回到命令行模式。 • (3)底行模式 • 在该模式下,光标位于屏幕的底行。用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等。
Vi • 命令模式:可以完成诸如粘贴、拷贝等命令
Vi • 插入模式:完成程序的编辑
Vi • 底行模式:比如完成文档底保存
Vi举例 • 为了快速获得对linux平台下应用程序设计的体验,我们首先编写经典的hello world程序。 • 首先,进入linux的控制台界面。 • 然后,在某个目录建立一个空的c程序文件,比如利用 touch /home/zhaohui/linuxApp/hello.c命令 • 然后cd到当前目录,调用vi编辑器对这个空文件进行编辑,输入这个程序的源代码,(利用vi hello.c命令,进入vi编辑器,然后进入vi的编辑状态,然后输入程序,然后进入vi的底行状态,进行源程序的保存和vi的退出,回到shell提示符。)
Vi举例 --------------hello.c---------------------- #include <stdio.h> int main() { printf("Hello, Linux programming world!\n"); return 0; } -------------------------------------------
Vi举例 • 在当前目录输入编译这个程序的命令,在程序没有错误的情况下,会在当前目录得到一个可执行文件hello。
Vi举例 • 这个命令会依次调用gcc的预编译器(cpp),编译器(生成.s文件),汇编器(生成.o文件)和链接器程序(ld)。
Vi举例 • 当然也可以使用objdump -d hello来查看汇编代码。 • 诸如objdump等这些命令称为binutils,是非常强大的代码分析工具。比如addr2line,ar,as,gprof,ld,nm,objcopy,objdump,ranlib,size,strings,strip等。 • 具体可以参考其man命令手册。
补充:头文件和函数库 • linux程序开发的头文件 • 函数库 • 静态库 • 共享库
头文件和函数库 • linux程序开发的头文件 • 函数库 • 静态库 • 共享库
1.头文件 • 一般而言,每个C++/C程序通常由头文件(header files)和定义文件(definition files)组成。 • 头文件作为一种包含功能函数、数据接口声明的载体文件,用于保存程序的声明(declaration),而定义文件用于保存程序的实现(implementation)。 • C++/C程序的头文件以“.h”为后缀。
头文件 • 对c语言来说,头文件一般存在于目录/usr/inlcude下面 • 而对于linux操作系统的头文件,一般存在于目录/usr/include/sys和/usr/include/linux下。 • 其他程序设计系统也可能有自己单独的目录,比如/usr/include/X11或者/usr/include/g++等。
头文件 • Gcc的一个可选参数:-I,用于在编译阶段,指定非标准位置的头文件。 • 比如:gcc –I /usr/***/include test.c会在指定的位置查找test.c文件所include的一些头文件。
2 函数库 • 定义:是一些预先编译好的函数的集合,那些函数都是按照可再使用的原则编写的。它们通常是一组相互关联的用来完成某项常见工作的函数构成(比如c库里面的标准输入输出函数、时间函数和数学函数等)。 • 库文件的含义:就是一组处于可以“拿来就用”的状态下的二进制目标代码。当有程序需要用的函数库中的某个函数的时候,就会通过include语句引用对此函数做出声明的头文件。 • 程序函数库可以使整个程序更加模块化,更容易重新编译,而且更方便升级。
库文件 • 编译器和链接程序负责把程序代码和库函数结合在一起成为一个独立的可执行程序。如果使用的不是标准的c语言运行库而是某个扩展库,则必须指定它的位置和名字(使用-I,-L和-l参数)。 • 在链接阶段,会搜索一些默认的目录,比如/lib和/usr/lib等来查找需要的库文件。
库文件 • 命名规则:一般是以lib打头,然后后面接上表示函数库功能的名字部分。比如libc、libm和libcap等。 • 类型:分成静态库和共享库,后缀名分别是a和so(一般在系统中的/lib中,两个版本都存在)。而在win平台下面,分别是lib和dll。
库文件 • 在编译程序的时候,为了保证链接的正常进行,可以告诉gcc库文件的位置(当然,标准的c库是不需要指定的,其他的非标准库则要指定)。 • Gcc的一个可选参数:-L,用于在链接阶段,指定非标准库文件的位置。 • 比如: • $gcc −o x11fred −L/usr/openwin/lib x11fred.c −lX11
3. 静态库 • 静态函数库(static libraries):是一个普通的目标文件的集合,也叫归档文件,一般用“.a”作为文件的后缀,比如libc.a和libX11.a等。 • 自己建立静态函数库的方法: • 编写库函数的时候,尽量把不同类型函数实现编写到不同的源代码文件里面; • 然后使用gcc的编译命令对各个文件进行独立编译,从而得到各自的目标文件; • 然后使用ar命令把各个目标文件打包在一起。
静态库:Example • 1)编写一个小函数库,这个函数库里面有两个函数。分别使用两个源代码文件。 • 2)各自代码如下: ――――――――fred.c-------------- #include <stdio.h> void fred(int arg) { printf("fred: you passed %d\n", arg); }
静态库:Example ――――――――bill.c-------------- #include <stdio.h> void bill(char *arg) { printf("bill: you passed %s\n", arg); }
静态库:Example 3)进行两个源代码文件的编译 $ gcc −c bill.c fred.c $ ls *.o bill.o fred.o
静态库:Example • 4)利用ar命令,把目标代码添加到一个库文件中去。 $ ar crv libfoo.a bill.o fred.o a − bill.o a − fred.o
静态库:Example • 5)为了让库的使用者可以重复利用库所提供的函数,要编写一个头文件,在其中声明库都是对外提供了哪些函数。如果另外一个程序员打算使用库所提供的服务,则必须在头文件中include这个头文件。
静态库:Example ――――――――lib.h-------------- /* This is lib.h. It declares the functions fred and bill for users */ void bill(char *); void fred(int);
静态库:Example • 6)编写一个测试程序,使用库中的一个函数。源代码如下: ―――――――program.c-------------- #include "lib.h" int main() { bill("Hello World"); exit(0); }
静态库:Example • 7)编译和运行 • ―――――――第一个方法-------------- • $ gcc −c program.c • $ gcc −o program program.o bill.o • ―――――――第二个方法-------------- • $ gcc −o program program.o libfoo.a • ―――――――第三个方法-------------- • $gcc −o program program.o −L. −lfoo • $ ./program • bill: you passed Hello World • $
静态库 • 静态库的优点:使用ELF格式的静态库函数生成的代码可以比使用共享函数库的程序运行速度上快一些。 • 静态库的缺点:如果我们在同一时间运行多个程序,而它们又都使用着来自同一个函数库里的函数时,内存里面就会有许多份同一个函数的拷贝,在程序文件本身里面也有许多份同样的拷贝。这些会造成大量的硬盘和内存空间的浪费。而使用共享库可以解决上面的两个问题。
4. 共享库 • 共享函数库(shared libraries):当一个可执行程序在启动的时候被加载的函数。 • 每个共享函数库都有个特殊的名字,称作“soname”。soname名字命名必须以“lib”作为前缀,然后是函数库的名字,然后是“.so”,最后是版本号信息。 • 共享库的存放位置和静态库是一样的。比如,你可以利用ls命令显示/lib目录下的库的名字,可能的一个输出是libc.so.6(它是一个软链接文件),它是c语言标准库的共享版本,6代表的是主版本号。
共享库特点 • 如果一个程序使用了共享库,它的链接方式如下: • 程序本身不再包括函数的代码,而只保存共享代码的调用线索(references),共享代码是在该程序运行的时候才加入到其中的。 • 当编译好的程序被加载到内存中准备执行的时候,函数的调用线索被解析(the function references are resolved),程序向共享库发出调用,共享库只在必要的时候才被加载到内存。
共享库优点 • 1.多进程使用同一函数库:通过这种方法,在内存里,系统就可以只保留一份共享库的拷贝供许多程序使用,在硬盘上也只需要保存一份拷贝就可以了。 • 2. 修改函数库不需重新连编:共享库的升级不再会影响到依赖于它的那些程序。比如,我们只需要修改从/lib/libc.so.6到实际库文件的升级版本的符号链接就可以了。
共享库的安装 • 安装一个新版本的函数库的时候,要先将这些函数库文件拷贝到一些特定的目录中,运行ldconfig就可以。ldconfig检查已经存在的库文件,然后创建soname的符号链接到真正的函数库,同时设置/etc/ld.so.cache这个缓冲文件。 • 例如,创建两个目标文件(a.o和b.o),然后创建一个包含a.o和b.o的共享函数库。
共享库的加载 • Linux中的加载器:对于linux系统而言,负责加载共享库并解析app程序中的函数调用线索的程序(也就是共享库的动态加载器)是ld.so或者ld-linux.so.2。 • 查找共享库的其他目录是在/etc/ld.so.conf文件里面配置的;如果这个文件被修改了(比如,系统里面添加了X11共享库),可以使用工具程序ldd来处理(其实就是让ld-linux.so.2重新读取配置文件的内容到内存中)。
ldd命令 • ldd命令:如果想了解某个程序要求使用的是哪一个共享库,可以使用工具程序ldd来查看。比如: • $ ldd program • libc.so.6 => /lib/libc.so.6 (0x4001a000) • /lib/ld−linux.so.2 => /lib/ld−linux.so.2 (0x40000000)
总结:函数库和头文件的保存位置 • 函数库 • /lib:系统必备共享函数库 • /usr/lib:标准共享函数库和静态函数库 • /usr/i486-linux-libc5/lib:libc5 兼容性函数库 • /usr/X11R6/lib:X11R6 的函数库 • /usr/local/lib:本地函数库 • 头文件 • /usr/include:系统头文件 • /usr/local/include:本地头文件