【Linux】动态库与静态库

打印 上一主题 下一主题

主题 651|帖子 651|积分 1953


1. 认识静态库与动态库



  • 静态库(.a):程序在编译链接的时候把库的代码链接到可实行文件中。程序运行的时候将不再需要静态库。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享利用库的代码。
  • 一个动态库链接的可实行文件仅仅包含它用到的函数入口地点的一个表,而不是外部函数所在目标文件的整个呆板码。
  • 在可实行文件开始运行从前,外部函数的呆板码由利用体系从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。
  • 动态库可以在多个程序间共享,所以动态链接使得可实行文件更小,节省了磁盘空间。利用体系接纳虚拟内存机制,允许物理内存中的一份动态库被 需要用到该库的所有历程 共用,节省了内存和磁盘空间。
本文测试准备代码:
  1. // ---------- add.h ----------
  2. #ifndef __ADD_H__
  3. #define __ADD_H__
  4. int add(int a, int b);
  5. #endif
  6. // ---------- add.c ----------
  7. #include "add.h"
  8. int add(int a, int b)
  9. {
  10.     return a + b;
  11. }
  12. // ---------- sub.h ----------
  13. #ifndef __SUB_H__
  14. #define __SUB_H__
  15. int sub(int a, int b);
  16. #endif
  17. // ---------- sub.c ----------
  18. #include "sub.h"
  19. int sub(int a, int b)
  20. {
  21.     return a - b;
  22. }
  23. // ---------- main.c ----------
  24. #include <stdio.h>
  25. #include "add.h"
  26. #include "sub.h"
  27. int main()
  28. {
  29.     int a = 10;
  30.     int b = 20;
  31.     printf("add(%d, %d) = %d\n", a, b, add(a, b));
  32.     a = 100;
  33.     b = 20;
  34.     printf("sub(%d, %d) = %d\n", a, b, sub(a, b));
  35.     return 0;
  36. }
复制代码
2. 手动创建并测试静态库

2.1 生成静态库

我们先利用下令生成一个静态库,并测试是否可以利用:
  1. # 把上面的代码拿过来
  2. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
  3. total 20
  4. -rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c
  5. -rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h
  6. -rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c
  7. -rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c
  8. -rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h
  9. # 生成 .o 文件,准备打包为静态库
  10. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c add.c -o add.o
  11. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c sub.c -o sub.o
  12. # 生成静态库
  13. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -rc libmymath.a add.o sub.o
  14. # ar 是 gnu 归档工具,常用于将目标文件打包为静态库
  15. # rc 表示(replace and create)
  16. # 查看静态库中的目录列表
  17. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -tv libmymath.a
  18. rw-r--r-- 0/0   1376 Jan  1 08:00 1970 add.o
  19. rw-r--r-- 0/0   1376 Jan  1 08:00 1970 sub.o
  20. # t:列出静态库中的文件
  21. # v:verbose 详细信息
  22. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc main.c -L. -lmymath
  23. # -L:指定库路径
  24. # -l:指定库名
  25. # 测试静态库
  26. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  27. add.c  add.h  add.o  a.out  libmymath.a  main.c  sub.c  sub.h  sub.o
  28. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out
  29. add(10, 20) = 30
  30. sub(100, 20) = 80
  31. # 测试目标文件生成后,静态库删掉,程序照样可以运行
  32. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ rm libmymath.a
  33. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out
  34. add(10, 20) = 30
  35. sub(100, 20) = 80
复制代码
2.2 打包静态库

如果想把这个静态库给别人利用,我们需要将 库(.a)和 头文件(.h)一起打包;如许别人在看到头文件的时候,就知道我们的库中封装了哪些方法。
利用 Makefile 进行自动化编译:
  1. # ---------- Makefile ----------
  2. libmymath.a:sub.o add.o
  3.         ar -rc $@ $^
  4. %.o:%.c
  5.         gcc -c $<
  6. .PHONY:clean
  7. clean:
  8.         rm -rf *.o output libmymath.a
  9. # 将相关文件打包到 output 文件夹
  10. .PHONY:output
  11. output:
  12.         mkdir output
  13.         cp -rf *.h output
  14.         cp libmymath.a output
复制代码
  1. # ubuntu 20.04 实机演示
  2. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
  3. total 24
  4. -rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c
  5. -rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h
  6. -rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c
  7. -rw-rw-r-- 1 ubuntu ubuntu 188 Apr 21 13:38 Makefile
  8. -rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c
  9. -rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h
  10. # 构建静态库
  11. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
  12. gcc -c sub.c
  13. gcc -c add.c
  14. ar -rc libmymath.a sub.o add.o
  15. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
  16. total 36
  17. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
  18. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
  19. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
  20. -rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
  21. -rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
  22. -rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
  23. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
  24. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
  25. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o
  26. # 打包
  27. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make output
  28. mkdir output
  29. cp -rf *.h output
  30. cp libmymath.a output
  31. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
  32. total 40
  33. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
  34. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
  35. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
  36. -rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
  37. -rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
  38. -rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
  39. drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output
  40. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
  41. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
  42. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o
  43. # 查看 output
  44. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree output/
  45. output/
  46. ├── add.h
  47. ├── libmymath.a
  48. └── sub.h
  49. 0 directories, 3 files
复制代码
2.3 利用静态库

我们已经将静态库以及相关头文件打包到了 output 文件夹,如今我们新建一个 TestStaticLib 文件夹进行测试:
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mkdir TestStaticLib
  2. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
  3. total 44
  4. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
  5. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
  6. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
  7. -rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
  8. -rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
  9. -rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
  10. drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output
  11. -rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
  12. -rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
  13. -rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o
  14. drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:56 TestStaticLib
  15. # 将 output 移动到 TestStaticLib 文件夹,并改名为 StaticLib
  16. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mv output TestStaticLib/
  17. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestStaticLib/
  18. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ mv output StaticLib
  19. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls
  20. StaticLib
复制代码
在 TestStaticLib 目录中新建测试文件:
  1. // ---------- test.c ----------
  2. #include <stdio.h>
  3. #include "add.h"
  4. #include "sub.h"
  5. int main()
  6. {
  7.     int a = 10;
  8.     int b = 20;
  9.     printf("add(%d, %d) = %d\n", a, b, add(a, b));
  10.     a = 100;
  11.     b = 20;
  12.     printf("sub(%d, %d) = %d\n", a, b, sub(a, b));
  13.     return 0;
  14. }
复制代码
利用 Makefile 进行自动化编译:
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls
  2. StaticLib  test.c
  3. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ touch Makefile
  4. # 编写 Makefile
  5. ubuntu:~/Linux/test_4_20/TestStaticLib$ vim Makefile
  6. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ cat Makefile
  7. test:test.c
  8.         gcc -o $@ $^ -I ./StaticLib -L ./StaticLib -l mymath
  9.         # -I:指定头文件所在目录
  10.         # -L:指定静态库所在目录
  11.         # -l:指定库名
  12. .PHONY:clean
  13. clean:
  14.         rm -f test
复制代码
测试:
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ make
  2. gcc -o test test.c -I ./StaticLib -L ./StaticLib -l mymath
  3. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls -l
  4. total 32
  5. -rw-rw-r-- 1 ubuntu ubuntu    99 Apr 21 14:21 Makefile
  6. drwxrwxr-x 2 ubuntu ubuntu  4096 Apr 21 13:43 StaticLib
  7. -rwxrwxr-x 1 ubuntu ubuntu 16816 Apr 21 14:21 test
  8. -rw-rw-r-- 1 ubuntu ubuntu   243 Apr 21 14:13 test.c
  9. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ./test
  10. add(10, 20) = 30
  11. sub(100, 20) = 80
复制代码
3. 库搜刮路径



  • 从左到右搜刮 -L 指定的目录
  • 由环境变量指定的目录(LIBRARY_PATH)
  • 由体系指定的目录

    • / user / lib
    • / user / local / lib

下面测试利用动态库时,需要我们在库搜刮路径中手动添加动态库!
4. 手动创建并测试动态库

4.1 生成动态库

利用下令手动生成静态库:
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  2. add.c  add.h  main.c  sub.c  sub.h
  3. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -fPIC -c sub.c add.c
  4. # fPIC:产生位置无关码(position independent code)
  5. # 编译产生的代码没有绝对位置,只有相对位置,从而可以在任意地方调用生成的动态库
  6. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  7. add.c  add.h  add.o  main.c  sub.c  sub.h  sub.o
  8. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -shared -o libmymath.so *.o
  9. # shared:表示生成共享库格式
  10. # 库名规则:lib*.so
  11. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  12. add.c  add.h  add.o  libmymath.so  main.c  sub.c  sub.h  sub.o
复制代码
4.2 打包动态库

将 动态库(.so)和 头文件(.h)打包到一个文件夹:
  1. # ---------- Makefile ----------
  2. libmymath.so:add.o sub.o
  3.         gcc -shared -o $@ $^
  4. %.o:%.c
  5.         gcc -fPIC -c $<
  6. .PHONY:clean
  7. clean:
  8.         rm -rf libmymath.so *.o DynamicLib
  9. .PHONY:DynamicLib
  10. DynamicLib:
  11.         mkdir DynamicLib
  12.         cp *.h DynamicLib
  13.         cp libmymath.so DynamicLib
复制代码
  1. # ubuntu 20.04 实机演示
  2. # 构建动态库
  3. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  4. add.c  add.h  main.c  Makefile  sub.c  sub.h  TestStaticLib
  5. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
  6. gcc -fPIC -c add.c
  7. gcc -fPIC -c sub.c
  8. gcc -shared -o libmymath.so add.o sub.o
  9. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  10. add.c  add.o         main.c    sub.c  sub.o
  11. add.h  libmymath.so  Makefile  sub.h  TestStaticLib
  12. # 打包
  13. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make DynamicLib
  14. mkdir DynamicLib
  15. cp *.h DynamicLib
  16. cp libmymath.so DynamicLib
  17. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
  18. add.c  add.o       libmymath.so  Makefile  sub.h  TestStaticLib
  19. add.h  DynamicLib  main.c        sub.c     sub.o
  20. # 查看打包文件
  21. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree DynamicLib/
  22. DynamicLib/
  23. ├── add.h
  24. ├── libmymath.so
  25. └── sub.h
  26. 0 directories, 3 files
复制代码
4.3 利用动态库

故技重施,创建 TestDynamicLib 文件夹进行测试:
  1. # 把上面的动态库拿过来,并创建 Makefile 和 test.c
  2. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree TestDynamicLib/
  3. TestDynamicLib/
  4. ├── DynamicLib
  5. │   ├── add.h
  6. │   ├── libmymath.so
  7. │   └── sub.h
  8. ├── Makefile
  9. └── test.c
  10. # test.c 与静态库保持一致
  11. # Makefile
  12. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cat TestDynamicLib/Makefile
  13. test:test.c
  14.         gcc -o $@ $^ -I ./DynamicLib -L ./DynamicLib -l mymath
  15. .PHONY:clean
  16. clean:
  17.         rm -f test
复制代码
直接编译试试?
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestDynamicLib/
  2. # 编译
  3. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ make
  4. gcc -o test test.c -I ./DynamicLib -L ./DynamicLib -l mymath
  5. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
  6. DynamicLib  Makefile  test  test.c
  7. # 运行
  8. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test
  9. ./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
复制代码
发现报错了,体系找不到我们的动态库,为什么?
上面我们提到过库搜刮路径,我们只是在编译时指定了路径,只有编译器知道动态库的位置,编译完成后就没有人知道去那里找动态库了!所以可实行程序是找不到动态库的!
那为什么静态库没事呢?因为静态库是直接把库链接到可实行文件中,编译完成后把静态库删掉都不会影响可实行文件
动态库不一样,程序每次实行都要去磁盘上搜刮动态库的位置,然后链接,由于我们并没有指定动态库的位置,所以找不到!
如何办理?
一个比力简单且没有副作用的方法是修改 LD_LIBRARY_PATH 环境变量,因为我们只是想测试,并不想真的把这个动态库添加到体系中,而环境变量在我们关掉 xshell 后就会重置,所以我们选择这种方法进行测试:
  1. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ pwd
  2. /home/ubuntu/Linux/test_4_20/TestDynamicLib
  3. # 修改环境变量
  4. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ export LD_LIBRARY_PATH=/home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLib
  5. # 查看环境变量
  6. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ echo $LD_LIBRARY_PATH
  7. /home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLib
  8. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
  9. DynamicLib  Makefile  test  test.c
  10. # 执行测试程序
  11. ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test
  12. add(10, 20) = 30
  13. sub(100, 20) = 80
  14. # 测试成功
复制代码
5. 动静态库优先级



  • 如果我们同时提供动态库和静态库,gcc 默认利用的是动态库;
  • 如果我们非要静态链接,我们必须利用 static 选项;
  • 如果我们只提供静态库,那我们的可实行程序也没办法,只能对该库进行静态链接,但是程序不肯定团体是静态链接的。
  • 如果我们只提供动态库,默认只能动态链接,如果非要静态链接,会发生链接报错!

   END
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小秦哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表