库 ------ 1. 增量编译——易于维护。 库易于使用。 2. 链接静态库是将库中的被调用代码复制到调用模块中, 而链接共享库则只是在调用模块中, 嵌入被调用代码在库中的(相对)地址。 3. 静态库占用空间非常大,不易修改但执行效率高。 共享库占用空间小,易于修改但执行效率略低。 4. 静态库的缺省扩展名是.a,共享库的缺省扩展名是.so。 六、静态库 ---------- 1. 创建静态库 ~~~~~~~~~~~~~ 1) 编辑源程序:.c/.h 2) 编译成目标文件:gcc -c xxx.c -> xxx.o 3) 打包成静态库文件:ar -r libxxx.a xxx.o ... # gcc -c calc.c # gcc -c show.c # ar -r libmath.a calc.o show.o ar指令:ar [选项] 静态库文件名 目标文件列表 -r - 将目标文件插入到静态库中,已存在则更新。 -q - 将目标文件追加到静态库尾。 -d - 从静态库中删除目标文件。 -t - 列表显示静态库中的目标文件。 -x - 将静态库展开为目标文件。 注意:提供静态库的同时也需要提供头文件。 2. 调用静态库 ~~~~~~~~~~~~~ # gcc main.c libmath.a (直接法) 或通过LIBRARY_PATH环境变量指定库路径: # export LIBRARY_PATH=$LIBRARY_PATH:. # gcc main.c -lmath (环境法) 或通过gcc的-L选项指定库路径: # unset LIBRARY_PATH # gcc main.c -lmath -L. (参数法) 一般化的方法:gcc .c/.o -l<库名> -L<库路径> 3. 运行 ~~~~~~~ # ./a.out 在可执行程序的链接阶段,已将所调用的函数的二进制代码, 复制到可执行程序中,因此运行时不需要依赖静态库。 范例:static/ 七、共享库 ---------- 1. 创建共享库 ~~~~~~~~~~~~~ 1) 编辑源程序:.c/.h 2) 编译成目标文件:gcc -c -fpic xxx.c -> xxx.o 3) 链接成共享库文件:gcc -shared xxx.o ... -o libxxx.so # gcc -c -fpic calc.c # gcc -c -fpic show.c # gcc -shared calc.o show.o -o libmath.so 或一次完成编译和链接: # gcc -shared -fpic calc.c show.c -o libmath.so PIC (Position Independent Code):位置无关代码。 可执行程序加载它们时,可将其映射到其地址空间的 任何位置。 -fPIC - 大模式,生成代码比较大,运行速度比较慢, 所有平台都支持。 -fpic - 小模式,生成代码比较小,运行速度比较快, 仅部分平台支持。 注意:提供共享库的同时也需要提供头文件。 2. 调用共享库 ~~~~~~~~~~~~~ # gcc main.c libmath.so (直接法) 或通过LIBRARY_PATH环境变量指定库路径: # export LIBRARY_PATH=$LIBRARY_PATH:. # gcc main.c -lmath (环境法) 或通过gcc的-L选项指定库路径: # unset LIBRARY_PATH # gcc main.c -lmath -L. (参数法) 一般化的方法:gcc .c/.o -l<库名> -L<库路径> 3. 运行 ~~~~~~~ 运行时需要保证LD_LIBRARY_PATH 环境变量中包含共享库所在的路径: # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. # ./a.out 在可执行程序的链接阶段, 并不将所调用函数的二进制代码复制到可执行程序中, 而只是将该函数在共享库中的地址嵌入到可执行程序中, 因此运行时需要依赖共享库。 范例:shared/ gcc缺省链接共享库,可通过-static选项强制链接静态库。 如:gcc -static hello.c 八、动态加载共享库 ------------------ #include <dlfcn.h> 1. 加载共享库 ~~~~~~~~~~~~~ void* dlopen ( const char* filename, // 共享库路径, // 若只给文件名, // 则根据LD_LIBRARY_PATH // 环境变量搜索 int flag // 加载方式 ); 成功返回共享库句柄,失败返回NULL。 flag取值: RTLD_LAZY - 延迟加载,使用共享库中的符号 (如调用函数)时才加载。 RTLD_NOW - 立即加载。 2. 获取函数地址 ~~~~~~~~~~~~~~~ void* dlsym ( void* handle, // 共享库句柄 const char* symbol // 函数名 ); 成功返回函数地址,失败返回NULL。 3. 卸载共享库 ~~~~~~~~~~~~~ int dlclose ( void* handle // 共享库句柄 ); 成功返回0,失败返回非零。 4. 获取错误信息 ~~~~~~~~~~~~~~~ char* dlerror (void); 有错误发生则返回错误信息字符串指针,否则返回NULL。 范例:load.c 注意:链接时不再需要-lmath,但需要-ldl(gcc -o load -ldl load.c)。 九、辅助工具 ------------ nm: 查看目标文件、可执行文件、静态库、 共享库中的符号列表。 ldd: 查看可执行文件和共享库的动态依赖。 ldconfig: 共享库管理。 事先将共享库的路径信息写入/etc/ld.so.conf配置文件中, ldconfig根据该配置文件生成/etc/ld.so.cache缓冲文件, 并将该缓冲文件载入内存,借以提高共享库的加载效率。 系统启动时自动执行ldconfig,但若修改了共享库配置, 则需要手动执行该程序。 strip: 减肥。去除目标文件、可执行文件、 静态库和共享库中的符号列表、调试信息等。 objdump: 显示二进制模块的反汇编信息。 # objdump -S a.out 指令地址 机器指令 汇编指令 -------- -------------------- --------------------- 8048514: 55 push %ebp 8048515: 89 e5 mov %esp,%ebp 8048517: 83 e4 f0 and $0xfffffff0,%esp 804851a: 83 ec 20 sub $0x20,%esp 804851d: c7 44 24 04 02 00 00 movl $0x2,0x4(%esp) load.c代码: #include <stdio.h> #include <dlfcn.h> typedef int (*PFUNC_CALC) (int, int); typedef void (*PFUNC_SHOW) (int, char, int, int); int main (void) { void* handle = dlopen ("shared/libmath.so", RTLD_NOW); if (! handle) { fprintf (stderr, "dlopen: %s\n", dlerror ()); return -1; } PFUNC_CALC add = (PFUNC_CALC)dlsym (handle, "add"); if (! add) { fprintf (stderr, "dlsym: %s\n", dlerror ()); return -1; } PFUNC_CALC sub = (PFUNC_CALC)dlsym (handle, "sub"); if (! sub) { fprintf (stderr, "dlsym: %s\n", dlerror ()); return -1; } PFUNC_SHOW show = (PFUNC_SHOW)dlsym (handle, "show"); if (! show) { fprintf (stderr, "dlsym: %s\n", dlerror ()); return -1; } show (30, '+', 20, add (30, 20)); show (30, '-', 20, sub (30, 20)); if (dlclose (handle)) { fprintf (stderr, "dlclose: %s\n", dlerror ()); return -1; } return 0; }
调用库
猜你喜欢
转载自946265172.iteye.com/blog/2199998
今日推荐
周排行