目录前言Part1. 库的意义Part2. 静态库制作、编译与发布Part2.1. 静态库制作步骤Part2.2. 使用静态库编译业务代码Part2.3. 静态库的打包分发Part2.4. Makefile 自动化构建静态库、一键打包发布Part3. 动态库制作、编译、部署Part3.1. 动态库制作Part3.2. 编译业务程序链接动态库Part3.3. 解决运行时找不到动态库的方案Part3.4. 强制链接静态库Part4. 静态库与动态库对比Part5. 补充拓展Part6. 结语前言Linux下对于动静态库的认识是非常重要的接下来来跟随小编的视角来看看吧。lets go!!!!!!!!Part1. 库的意义在工程开发中我们会把可复用的功能封装成库不需要每次都重新编译源码。• 源码不需要对外交付只需要交付头文件.h用于给用户声明函数 库文件.a / .so存放编译好的机器指令•多个项目可以复用同一套库大幅减少重复编译的成本• 版本迭代时只需要更新库文件业务代码无需改动补充目标文件 .o 的价值一个大型工程有上百个 .c 文件如果只修改其中一个源码只需要重新编译这一个文件生成 .o其余目标文件可以直接复用避免全量重新编译节省编译时间。编译链路.c → 编译生成 .o可重定位目标文件ELF格式 → 链接 → 可执行程序 / 库底层小知识.o、静态库、动态库、可执行程序全部都是ELF格式。ELF内部由大量的section段组成代码段、数据段、只读段等运行时操作系统会把多个section合并为segment段分区映射到进程的虚拟地址空间。Part2. 静态库制作、编译与发布静态库后缀命名规范libxxx.a比如我们示例库名字为 libmyc.aPart2.1. 静态库制作步骤1. 先把源码编译为目标文件 .o# -c只编译不链接生成 *.o gcc -c 1.c -o 1.o gcc -c 2.c -o 2.o2. 使用 ar 工具打包多个 .o归档成为静态库 .a# ar -rc r插入/替换c创建归档文件 ar -rc libmyc.a 1.o 2.oar 本质就是一个归档工具把一堆 .o 打包成一个文件静态库本身就是一堆目标文件的压缩包gcc链接的时候会把库里面用到的目标代码直接拷贝进最终的可执行文件。Part2.2. 使用静态库编译业务代码假设业务代码 main.c 需要调用我们静态库里面的函数。编译链接语法gcc -o exe main.c -I ./include -L ./mylib -l myc参数解释参数 作用-I路径 指定头文件搜索路径用来找到 .h-L路径 指定库文件.a/.so的搜索路径-lxxx 链接名为 libxxx.a / libxxx.so 的库自动补全lib前缀和后缀Part2.3. 静态库的打包分发我们交付给用户的时候不需要交付 .c 源码只需要交付• include/存放 .h 头文件函数声明• mylib/存放编译好的 libmyc.a 静态库用户拿到之后有两种方式使用头文件1. 编译时通过 -I 指定头文件目录gcc -c main.c -I ./lib/include2. 或者直接把头文件拷贝到系统标准头文件目录 /usr/includecp lib/include/* /usr/include库文件拷贝到系统库目录 /usr/libcp lib/mylib/libmyc.a /usr/lib之后编译就可以简化不需要写 -I -L直接gcc -o exe main.c -l mycPart2.4. Makefile 自动化构建静态库、一键打包发布可以用Makefile自动化完成编译、打包、创建目录、打包压缩交付包libmyc.a: 1.o 2.o ar -rc $ $^ 1.o:1.c gcc -c $ -o $# 发布目标自动创建目录拷贝头文件、库打包tar压缩包.PHONY: output output: mkdir -p lib/include mkdir -p lib/mylib cp -f *.h lib/include cp -f libmyc.a lib/mylib tar -czf lib.tgz lib执行make output直接生成 lib.tgz这个压缩包就可以发给别人使用。静态库核心特点链接的时候代码直接复制进exe。一旦编译完成后续静态库文件就算删除程序依旧可以正常运行。Part3. 动态库制作、编译、部署动态库后缀.so命名示例 libmyc.so动态库不会把代码拷贝进可执行文件程序运行的时候操作系统才去加载 .so多个进程可以共享同一份动态库在内存中的副本节省内存。Part3.1. 动态库制作1. 编译目标文件必须带上 -fPIC生成位置无关代码gcc -fPIC -c 1.c -o 1.o gcc -fPIC -c 2.c -o 2.o-fPICPosition Independent Code 位置无关代码生成的机器指令没有硬编码绝对地址可以被加载到虚拟地址空间的任意位置是动态库的核心。静态库不需要 -fPIC。2. 使用 -shared 参数把 .o 链接打包为动态库 .sogcc -shared -o libmyc.so 1.o 2.oPart3.2. 编译业务程序链接动态库编译链接阶段语法和静态库几乎一模一样gcc -o exe main.c -I ./include -L ./mylib -l myc这里有一个非常经典的坑编译链接成功但是运行的时候报错找不到动态库原因-L 只是编译阶段告诉gcc去哪里找库程序运行阶段是操作系统去加载动态库和gcc已经无关了操作系统有自己一套动态库搜索路径。Part3.3. 解决运行时找不到动态库的方案方案1临时环境变量 LD_LIBRARY_PATHLD_LIBRARY_PATH 是Linux操作系统的动态库环境变量操作系统运行程序的时候会优先从这个变量记录的目录里面搜索 .so。# 临时生效当前终端窗口有效关闭终端就失效 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/home/xxx/lib/mylib ./exe方案2写入系统动态库配置文件永久生效Linux系统动态链接器会读取 /etc/ld.so.conf.d/ 下面的配置文件1. 在 /etc/ld.so.conf.d/ 新建一个配置文件随便命名例如 myconfig.conf写入我们动态库的绝对路径/home/xxx/lib/mylib2.更新动态链接器缓存ldconfig永久生效所有用户、所有终端都可以找到这个动态库方案3直接拷贝动态库到系统标准库目录/usr/libcp libmyc.so /usr/lib ldconfigPart3.4. 强制链接静态库如果你本身系统同时存在 .a 和 .sogcc默认优先链接动态库。如果你想要强制编译的时候使用静态库增加 -static 参数gcc -o exe main.c -I ... -L ... -l myc -staticPart4. 静态库与动态库对比特性静态库 .a动态库 .so链接时期编译链接阶段代码拷贝进可执行文件编译阶段只记录符号运行时操作系统加载程序体积可执行文件更大包含库代码可执行文件更小内存占用每个进程都有一份独立的库代码副本内存占用高内存中只加载一份多进程共享节省内存更新方式更新库业务代码必须重新编译链接替换 .so 文件即可不需要重新编译业务程序依赖编译完成后不再依赖原 .a 文件运行时必须保证 .so 存在否则程序无法启动工程选型Linux后端服务绝大多数场景优先使用动态库一些嵌入式、想要单文件交付、不想携带一堆so的场景会使用静态库。Part5. 补充拓展1. .o 可重定位目标文件编译产物还没有分配虚拟地址等待链接器完成地址重定位2. ELF 文件内部划分为很多 section.text代码段、.data数据段、.rodata只读段、.bss未初始化数据段3. 当程序被操作系统加载运行时内核会把多个section合并成segment内存段映射到进程独立的虚拟地址空间这就是进程的内存布局。Part6. 结语这篇文章我们深入的知道了动静态库的相关知识接下来小编还会带来更多Linux相关的知识敬请期待~最后祝大家可以春风得意马蹄疾一日看尽长安花最后的最后要是觉得本文还可以的话可以点点赞关注小编一波谢谢大家~