2.37k likes | 2.52k Views
嵌入式系统程序设计. 大连理工大学软件学院 嵌入式系统工程系 赖晓晨. L inux库. ★. ★. ★. ★. L inux库概述 库操作工具 静态库 共享库 动态链接库. 一、 L inux库概述. 库的概念:库 是操作系统或者编译器提供的一种目标文件,是可以被多个软件项目使用的二进制 代码集。 库是代码重用思想的体现。 使用库可以节约编程人员大量的时间 。. 库分类. 静态库,是在执行程序运行前就已经加入到执行码中,在物理上成为执行程序的一部分。 共享库,是在执行程序启动时加载到执行程序中,可以被多个执行程序共享使用。
E N D
嵌入式系统程序设计 大连理工大学软件学院 嵌入式系统工程系 赖晓晨
Linux库 ★ ★ ★ ★ • Linux库概述 • 库操作工具 • 静态库 • 共享库 • 动态链接库
一、Linux库概述 • 库的概念:库是操作系统或者编译器提供的一种目标文件,是可以被多个软件项目使用的二进制代码集。 • 库是代码重用思想的体现。 • 使用库可以节约编程人员大量的时间。
库分类 • 静态库,是在执行程序运行前就已经加入到执行码中,在物理上成为执行程序的一部分。 • 共享库,是在执行程序启动时加载到执行程序中,可以被多个执行程序共享使用。 • 动态链接库,其实并不是一种真正的库类型,应该是一种库的使用技术,应用程序可以在运行过程中随时加载和使用库。
库命名约定 • 所有库都以lib开头,表示一个库文件; • 文件名以.a结尾的是静态库,以.so结尾的是共享库。
共享库库名称约定 • 共享库命名规则稍显繁琐,我们来区分一下一下命名: • real name:包含库名、主版本号、次版本号以及发布号等字段,它代表文件中包含了库的代码,这是库的实体文件。 • soname(shared object name):库文件的一个符号链接,一般仅包括库名和主版本号。 • linkername:库文件的符号链接,它仅包含库名,一般供编译器使用。
共享库库名称约定(续) • 看下面的例子: 在以上信息中: • libpng.so.2.1.0.12是共享库的实名(realname) • libpng.so.2是共享库so名(soname) • libpng.so则是连接名(linkername),用于编译连接。 #ls -l *png* lrwxrwxrwx1rootrootlibpng.so->libpng12.so lrwxrwxrwx1rootrootlibpng.so.2->libpng.so.2.1.0.12 -rw-r--r--1rootrootlibpng.so.2.1.0.12
常用库 库 头文件 描述 libc.so 无 标准C库 libdb.so db.h 数据库库 libm.so math.h 数学库 libpthread.so pthread.h 多线程库 libz.so zlib.h 压缩例程库 libvga.so vga.h 底层图形库 libcom_err.so com_err.h 出错处理库 libdl.so dlfcn.h 动态加载库
二、库操作工具 • Linux系统提供很多对库进行操作的工具,具体如下: • nm命令 • ar命令 • ldd命令 • ldconfig命令
1. nm命令 • 用途:列出库或目标文件的所有符号。 • 查看程序调用什么函数 • 查看一个给定的库或者目标文件是否提供了所需的函数。 • 举例:下图列出了libc.so.6中所有包含sprintf的字符串的符号。
2. ar命令 • 用途:可以建立一个归档文件,通常用来创建静态库。 • 举例:建立静态库 ar rc libmy.a file1.o file2.o …
3. ldd命令 • 用途:列出为程序正常运行所需要的共享库。 • 举例:利用ldd命令查看bash所依赖的库文件,如下图所示:
4. ldconfig命令 • 当为系统安装好库文件之后,需要运行ldconfig命令,其功能是根据/etc/ld.so.conf中的路径检查库文件,并为它们创建相应的soname,然后更改ld.so.cache文件。
ldconfig命令(续) • 应用程序执行时,/lib目录下的程序ld-linux.so.X(X是版本号)会首先被运行,这就是Linux系统的程序装载器。 • 装载器负责检查应用程序需要使用的共享库,从ld.so.conf指定的目录中找到这些库并加载,然后把加载信息写入缓存文件/lib/ld.conf.cache中供其他程序使用,以提高系统运行效率。
ldconfig命令(续) • Linux系统下的一个环境变量: • $LD_LIBRARY_PATH:是一个由冒号分隔的目录清单,包含了运行时的共享库,可以指示ld.so到何处搜索没有保存在标准位置的库。 (对应文件为:/etc/ld.so.conf)
三、静态库 • 静态库一般命名为libxxx.a,采用静态编译得到的程序文件比较大,因为整个函数库都已经被链接到程序中。 • 静态库实质是目标文件的集合,采用ar命令可以创建静态库。首先编写源文件,然后把源文件分别编译为目标文件,注意使用-c选项,然后用ar命令把目标文件归档为一个静态库文件。
【例6-1】 /* ch6_1 main.c */ void hello(); void bye(); #include <stdio.h> int main() { hello(); bye(); return 0; } 静态库源文件:
【例6-1】(续) /* ch6_1 hello.c */ #include <stdio.h> void hello () { printf("hello everybody!\n"); } /* ch6_1 bye.c */ #include <stdio.h> void bye() { printf("goodbye!\n"); } 静态库源文件:
四、共享库 • 共享库一般命名为libxxx.so,与静态库不同,共享库并不会在程序编译时被添加到可执行文件中,而是在程序执行时才会被链接,因此采用动态编译方式得到的可执行程序文件比较小,但是程序的执行依赖于环境,当前操作系统中必须存在程序需要的共享库,否则程序不能执行。 • 编译共享库时要用gcc的-shared和-fPIC选项,前者表示编译为共享库,后者表示把库文件编译成位置无关代码,否则将来程序无法运行。
共享库(续) 用ldd命令检查main程序运行时所需要的动态库,结果如下图所示:
五、动态链接库 • 打开共享库 • 提取函数地址 • 关闭共享库 • 共享库错误函数
1. 打开共享库 • 函数原型: void *dlopen ( const char *libname, int flag); • 功能描述:将libname代表的库装载到内存,flag是打开共享库的方式。如果函数调用成功,返回库句柄,如果该库已经被装载过,则返回同样的句柄,如果调用失败,返回NULL。如果要装载的库依赖其他库,必须先装载依赖库。
打开共享库(续) • 功能描述:参数中的libname一般是库的绝对路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻: • 根据环境变量LD_LIBRARY_PATH查找; • 根据/etc/ld.so.cache查找; • 依次在/lib和/usr/lib目录查找。 • flag参数表示处理未定义函数的方式 • RTLD_LAZY:暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再报错 • RTLD_NOW:马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。 • RTLD_GLOBL:使共享库定义的符号全局可见。
2. 提取函数地址 • 函数原型: void *dlsym(void *handle, char *symbol) • 功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。
3. 关闭共享库 • 函数原型为: int dlclose (void *handle); • 功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。
4. 共享库错误函数 • 函数原型: const char *dlerror(void); • 功能描述:当动态链接库操作函数(dlopen、dlsym、dlclose)执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
【例6-2】 /* ch6_2 sub.h */ #ifndef SUB_H #define SUB_H int square(int); #endif /* ch6_2 sub.c */ #include <stdio.h> int square(int a) { printf("the square of the number is:"); return a*a; } 源文件
【例6-2】(续) /* ch6_2 main.c */ #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include "sub.h" int main() { void *handle; int (*fp)(int); char *error; int n; int result; printf("please input a number.\n"); scanf("%d",&n); 声明函数指针,将来用来执行希望使用的函数
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 打开动态链接库 RTLD_LAZY: 只有在使用时才检查错误
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 如未成功,handle为NULL,则显示错误信息
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 找到需要的函数
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 如未找到函数,显示error中的错误信息
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 用函数指针调用相应函数
handle=dlopen("/lib/libmydll.so", RTLD_LAZY); if(!handle) { printf("%s\n",dlerror()); } fp=dlsym(handle, "square"); if((error=dlerror())!=NULL) { printf("%s\n",error); dlclose(handle); exit(1); } printf("now call the function square.\n"); result = (*fp)(n); printf(" %d\n",result); dlclose(handle); return 0; } 关闭库
动态链接库建立步骤 • 建立动态链接库 gcc –shared –fPIC –o libmydll.so sub.c -wall
动态链接库建立步骤 • 建立动态链接库 • 移动动态链接库到系统标准库目录 gcc –shared –fPIC –o libmydll.so sub.c -wall mv libmydll.so /lib
动态链接库建立步骤 • 建立动态链接库 • 移动动态链接库到系统标准库目录 • 生成可执行文件 gcc –shared –fPIC –o libdy2.so k1.c k2.c mv libdy2.so /lib gcc main.c –o main –lmydll -ldl -Wall
动态链接库建立步骤 • 建立动态链接库 • 移动动态链接库到系统标准库目录 • 生成可执行文件 • 执行文件 gcc –shared –fPIC –o libmydll.so sub.c -wall mv libdy2.so /lib gcc main.c –o main–lmydll2 -ldl -Wall ./main
嵌入式Linux操作系统 • 嵌入式Linux操作系统简介 • 启动引导程序vivi • CRAMFS文件系统 • 构建嵌入式Linux系统
一、嵌入式Linux操作系统简介 • 嵌入式Linux是以Linux为基础,经过裁剪之后适用于嵌入式设备的操作系统,广泛应用在移动电话、PDA、媒体播放器、消费性电子产品以及航空航天等领域。Linux系统具有开源、可裁剪、免费、完全支持TCP/IP协议、可移植性好、运行稳定等特点,嵌入式Linux继承了这些特性。
嵌入式Linux操作系统简介(续) • 与uClinux系统相同,基本的嵌入式Linux操作系统包括三个组成部分:Bootloader,负责引导系统;操作系统内核,负责管理硬件,为上层软件提供运行环境;文件系统,负责管理文件。
Linux内核源码目录树 • arch:包含所有体系结构相关的内核代码,可以在其中找到Linux目前支持的所有硬件体系结构; • kernel:包含Linux内核代码。 • drivers:包含设备驱动程序源代码; • fs:包含文件系统实现代码;
Linux内核源码目录树(续) • include:包含各种体系结构下编译内核所需要的头文件; • init:包含内核初始化源代码; • ipc:包含进程间通信源代码; • mm:包含内存管理源代码; • net:包括网络相关源代码。
二、启动引导程序vivi • vivi简介 • vivi的工作过程 • vivi的工作模式 • vivi的命令接口 • vivi命令实现的数据结构