资源预览内容
第1页 / 共13页
第2页 / 共13页
第3页 / 共13页
第4页 / 共13页
第5页 / 共13页
第6页 / 共13页
第7页 / 共13页
第8页 / 共13页
第9页 / 共13页
第10页 / 共13页
亲,该文档总共13页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
周易成记 Lib 库使用学习库使用学习笔记笔记 一、一、 Chapter 1. 为什么使用库文件为什么使用库文件 我们在实际编程工作中肯定会遇到这种情况:有几个项目里有一些函数模块的功能相同, 实现代码也相同,也是我们所说的重复代码。比如,很多项目里都有一个用户验证的功能。 代码段如下: /UserLogin.h 文件,提供函数声明 int IsValidUser(char* username, int namelen); /UserLogin.c 文件,实现对用户信息的验证 int IsValidUser(char* username, int namelen) int IsValid = 0; /*下面是具体的处理代码,略去*/ return IsValid; 如果每个项目都保存着这两个 UserLogin.h 和 UserLogin.c 文件,会有以下几个弊端: 1.每个项目里都有重复的模块,造成代码重复。 2.代码的重用性不好,一旦 IsValidUser 的代码发生了变化,为了保持设计的一致性, 我们还要手工修改其他项目里的 UserLogin.c 文件,既费时又费力,还容易出错。 库文件就是对公共代码的一种组织形式。 为了解决上面两个弊端,就提出了用库文件存放公共代码的解决方案,其要点就是把公共 的(也就是可以被多次复用的)目标代码从项目中分离出来,统一存放到库文件中,项目 要用到这些代码的时候,在编译或者运行的时候从库文件中取得目标代码即可。库文件又 分两种:静态库和动态库。 二、二、 Chapter 2. 静态库和动态库静态库和动态库 简单的说,如果程序是在编译时加载库文件的,就是使用了静态库,静态库的文件名格式 是“lib*.a“。如果是在运行时加载目标代码,就成为动态库,动态库的文件名格式是 “lib*.so.*“。换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码 段,程序的体积会膨胀。如果使用动态库,则程序中只保留库文件的名字和函数名,在运 行时去查找库文件和函数体,程序的体积基本变化不大。 静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;动态库则是“以时间换空 间”,增加了运行时间,但减少了程序本身的体积。在附录的 PIC 部分,也会做一些说明。 周易成记 从文件的格式角度讲,静态库的本质是一种档案文件(.o 文件的集合) ,其中包含了一个内 容索引 (也可以不包含,但没有索引的静态库不能用来链接,在附录的 ar 和各个模块既. o 文件;而动态库是 ELF 格式的文件,可以被用来加载和执行,而静态库不可以。 还有一种库文件,共享库。看到网上有些资料说,动态库就是共享库的一种变种, 由于没 有使用到,没有详细研究。 有时候,会在目录中看到以“.la“或“.lo“结尾的文件,这些是 GNU 的工具 libtool 生成和使用 的文件, 用来说明实际库文件的使用信息和以来关系,详细的内容会在以后 automake autoconf and libtool 的文档中介绍。 三、三、 Chapter 3. 静态库的生成和使用静态库的生成和使用 3.1. 制作最简单的静态库文件制作最简单的静态库文件 编写如下两个文件,放在同一目录中: mylib.h /静态库头文件 void test(); mylib.c /静态库实现文件 #include void test() printf(“hello world./n“); 使用下边的命令生成静态库: gcc -c mylib.c ar rc libmy.a mylib.o 这样就生成了静态库 libmy.a,注意一定要以 lib*.a 这样的格式命名,否则链接器 ld 不能识 别。 使用命令“file libmy.a“看看它的格式,是一个档案文件。我们可以使用 nm 查看它的 内部构成: root Benson libtest# nm libmy.a libmy.o: U printf 00000000 T test 这表示静态库有模块 libmy.o,在使用的时候,gcc 会根据需要将函数名得到模块,然后从 周易成记 静态库中 提取出对应的“.o“文件的内容,然后用来链接,就是使用单独的“.o“文件一样。 3.2. 使用库文件使用库文件 编写一个测试程序 main.c,内容为: #include “mylib.h“ int main(void) test(); return 0; 由于需要调用 libmy.a 中的 test 函数,所以在编译时,需通过“-L -l“参数指定链接这个库: gcc -I./ -o main main.c -L./ -lmy 通过-I 和-L 参数制定了 gcc 的头文件和库文件搜索路径为当前目录,也可以根据需要指定 为其他目录。 生成执行文件 main 后,执行命令“file main“,可以看到: root Benson libtest# file main main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, staticlly linked (uses shared libs), not stripped 运行,看到结果,成功。 四、四、 Chapter 4. 动态库的生成和使用动态库的生成和使用 4.1. 制作最简单的动态库文件制作最简单的动态库文件 我们同样使用上边的 mylib.h 和 mylib.c 文件,但使用不同的命令生成库文件: gcc -fpic -shared libmy.so libmy.c 这样就生成了动态库文件 libmy.so,-fpic 这个选项指定是否使用 PIC,这个选项的使用需要 系统平台的支持,一般建议添加,如果不支持,gcc 会报错。 生成 libmy.so 后,使用“file libmy.so“命令,可以看到是一个 ELF 格式的文件,这说明共享库 的 使用需要通过符号解析和重定位加载入内存才能使用。 4.2. 动态库的隐式调用动态库的隐式调用 动态库有两种使用方法,隐式调用和显示调用。隐式调用的方法跟静态库的使用方法一样, 都是通过 gcc 的“-I -L“参数指定库文件的路径,如果同一个库文件,在系统中同时存在静态 周易成记 库和动态库,默认情况下 gcc 主动链接动态库。但也可以通过 gcc 的“-static“选项,强制指 定使用静态库。 gcc -I./ -o main main.c -L./ -lmy # 使用动态库生成 main gcc main.c ./libmy.so -o main # 使用动态库生成 main,不同的地方在于指定了 libmy.so 的加载路径,这种用法很少用。详细的内容附录 ld.so gcc -static -I./ -o main main.c -L./ -lmy # 使用静态库生成 main 输入“file main“命令,可以看到: root Benson libtest# file main main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped 运行程序,报错: root Benson libtest# ./main ./main: error while loading shared libraries: libmy.so: cannot open shared object file: No such file or directory 没有找到 libmy.so 这个动态库,我这个库文件就在当前目录下? 4.3. 使动态库被系统共享的方法使动态库被系统共享的方法 前边已经说过动态库需要加载到内存中,才能使用。注意,链接和加载是两码事, 链接发 生在生成可执行程序的阶段,加载发生在运行阶段。gcc 的“-I -L“参数只是保证在链接阶段 指定文件路径,和加载无关,当然也有办法可以使链接阶段的路径信息在加载阶段起作用, 这些涉及到 gcc 的详细用法,和可执行文件的格式,附录中会简单介绍一些。运行时的加 载的工作 不是由 main 完成的,而是由/lib/ld.so(不同平台会有些出入)完成。他有自己 的查找动态连接库的规则, 所以在不更新 ld.so 的查找路径的情况下,会出现上边的错误。 根据 ld.so 的 man 手册,ld.so 共有六种方式查找需要的动态库,这里只介绍常用的三种方 式,详细的 内容见附录 ld.so 1.按照环境变量 LD_LIBRARY_PATH 的内容查找(setuid 类的程序排除) 2.在库高速缓存文件 ld.so.conf 中给出的路径查找 3.在/lib,/usr/lib 目录下查找 相对应的,可以分别通过设置环境变量 LD_LIBRARY_PATH,通过在 ld.so.conf 文件中添加路 径和将 动态库文件复制到/lib、/usr/lib 目录下,使动态库可以在加载时被找到。 root Benson libtest# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/pwd root Benson libtest# cat pwd /etc/ld.so.conf ; ldconfig root Benson libtest# cp libmy.so /lib -f 以上三种方式都可以工作。关于 ldconfig 的使用详解,见 附录 ldconfig 周易成记 此时再执行 main,可以正常运行了。 通过 ldd 命令,可以看到 main 程序依赖于 libmy.so。 root Benson libtest# ldd main libtest.so = not found libc.so.6 = /lib/tls/libc.so.6 (0x42000000) /lib/ld-linux.so.2 = /lib/ld-linux.so.2 (0x40000000) 4.4. 动态库的显式调用动态库的显式调用 动态库的显示调用,也需要库文件被加载到内存中。但是使用方法和静态库完全不同。需 要系统 调用dlopen dlsym dlclose dlerror 的支持。可以使用同一个 libmy.so 文件,但是 需要新的测试文件 : /* main.c 测试动态库显式调用的程序 */ #include /用于动态库管理的系统头文件 #include “libmy.h“ /要把函数的头文件包含进来,否则编译时会报错 int main(void) /* 声明对应的函数的函数指针 */ void (*pTest)(); /* 加载动态库 */ void *pdlHandle = dlopen(“libtest.so“, RTLD_LAZY); /* 错误处理 */ if(pdlHandle = NULL ) printf(“Failed load library/n“); return -1; char* pszErr = dlerror(); if(pszErr != NULL) printf(“%s/n“, pszErr); return -1; /* 获取函数的地址 */ pTest = dlsym(pdlHandle, “test“); pszErr = dlerror(); if(pszErr != NULL) 周易成记 printf(“%s/n“, pszErr); dlclose(pdlHandle); return -1; /* 实现函
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号