当前位置:首页 » 《关于电脑》 » 正文

【Linux】动态库与静态库

11 人参与  2024年12月03日 08:01  分类 : 《关于电脑》  评论

点击全文阅读


文章目录

1. 认识静态库与动态库2. 手动创建并测试静态库2.1 生成静态库2.2 打包静态库2.3 使用静态库 3. 库搜索路径4. 手动创建并测试动态库4.1 生成动态库4.2 打包动态库4.3 使用动态库 5. 动静态库优先级
在这里插入图片描述

1. 认识静态库与动态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制,允许物理内存中的一份动态库被 需要用到该库的所有进程 共用,节省了内存和磁盘空间。

本文测试准备代码:

// ---------- add.h ----------#ifndef __ADD_H__#define __ADD_H__int add(int a, int b);#endif// ---------- add.c ----------#include "add.h"int add(int a, int b){    return a + b;}// ---------- sub.h ----------#ifndef __SUB_H__#define __SUB_H__int sub(int a, int b);#endif// ---------- sub.c ----------#include "sub.h"int sub(int a, int b){    return a - b;}// ---------- main.c ----------#include <stdio.h>#include "add.h"#include "sub.h"int main(){    int a = 10;    int b = 20;    printf("add(%d, %d) = %d\n", a, b, add(a, b));    a = 100;    b = 20;    printf("sub(%d, %d) = %d\n", a, b, sub(a, b));    return 0;}

2. 手动创建并测试静态库

2.1 生成静态库

我们先使用命令生成一个静态库,并测试是否可以使用:

# 把上面的代码拿过来ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -ltotal 20-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h-rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h# 生成 .o 文件,准备打包为静态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c add.c -o add.oubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c sub.c -o sub.o# 生成静态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -rc libmymath.a add.o sub.o# ar 是 gnu 归档工具,常用于将目标文件打包为静态库# rc 表示(replace and create)# 查看静态库中的目录列表ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -tv libmymath.a rw-r--r-- 0/0   1376 Jan  1 08:00 1970 add.orw-r--r-- 0/0   1376 Jan  1 08:00 1970 sub.o# t:列出静态库中的文件# v:verbose 详细信息ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc main.c -L. -lmymath# -L:指定库路径# -l:指定库名# 测试静态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.h  add.o  a.out  libmymath.a  main.c  sub.c  sub.h  sub.oubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out add(10, 20) = 30sub(100, 20) = 80# 测试目标文件生成后,静态库删掉,程序照样可以运行ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ rm libmymath.a ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out add(10, 20) = 30sub(100, 20) = 80

2.2 打包静态库

如果想把这个静态库给别人使用,我们需要将 库(.a)和 头文件(.h)一起打包;这样别人在看到头文件的时候,就知道我们的库中封装了哪些方法。

使用 Makefile 进行自动化编译:

# ---------- Makefile ----------libmymath.a:sub.o add.oar -rc $@ $^%.o:%.cgcc -c $<.PHONY:cleanclean:rm -rf *.o output libmymath.a# 将相关文件打包到 output 文件夹.PHONY:outputoutput:mkdir outputcp -rf *.h outputcp libmymath.a output
# ubuntu 20.04 实机演示ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -ltotal 24-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h-rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c-rw-rw-r-- 1 ubuntu ubuntu 188 Apr 21 13:38 Makefile-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h# 构建静态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ makegcc -c sub.cgcc -c add.car -rc libmymath.a sub.o add.oubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -ltotal 36-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o# 打包ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make output mkdir outputcp -rf *.h outputcp libmymath.a outputubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -ltotal 40-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefiledrwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o# 查看 outputubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree output/output/├── add.h├── libmymath.a└── sub.h0 directories, 3 files

2.3 使用静态库

我们已经将静态库以及相关头文件打包到了 output 文件夹,现在我们新建一个 TestStaticLib 文件夹进行测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mkdir TestStaticLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -ltotal 44-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefiledrwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.odrwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:56 TestStaticLib# 将 output 移动到 TestStaticLib 文件夹,并改名为 StaticLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mv output TestStaticLib/ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestStaticLib/ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ mv output StaticLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ lsStaticLib

TestStaticLib 目录中新建测试文件:

// ---------- test.c ----------#include <stdio.h>#include "add.h"#include "sub.h"int main(){    int a = 10;    int b = 20;    printf("add(%d, %d) = %d\n", a, b, add(a, b));    a = 100;    b = 20;    printf("sub(%d, %d) = %d\n", a, b, sub(a, b));    return 0;}

使用 Makefile 进行自动化编译:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ lsStaticLib  test.cubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ touch Makefile# 编写 Makefileubuntu:~/Linux/test_4_20/TestStaticLib$ vim Makefileubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ cat Makefile test:test.cgcc -o $@ $^ -I ./StaticLib -L ./StaticLib -l mymath# -I:指定头文件所在目录# -L:指定静态库所在目录# -l:指定库名.PHONY:cleanclean:rm -f test

测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ makegcc -o test test.c -I ./StaticLib -L ./StaticLib -l mymathubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls -ltotal 32-rw-rw-r-- 1 ubuntu ubuntu    99 Apr 21 14:21 Makefiledrwxrwxr-x 2 ubuntu ubuntu  4096 Apr 21 13:43 StaticLib-rwxrwxr-x 1 ubuntu ubuntu 16816 Apr 21 14:21 test-rw-rw-r-- 1 ubuntu ubuntu   243 Apr 21 14:13 test.cubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ./test add(10, 20) = 30sub(100, 20) = 80

3. 库搜索路径

从左到右搜索 -L 指定的目录由环境变量指定的目录(LIBRARY_PATH)由系统指定的目录 / user / lib/ user / local / lib

下面测试使用动态库时,需要我们在库搜索路径中手动添加动态库!

4. 手动创建并测试动态库

4.1 生成动态库

使用命令手动生成静态库:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.h  main.c  sub.c  sub.hubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -fPIC -c sub.c add.c# fPIC:产生位置无关码(position independent code)# 编译产生的代码没有绝对位置,只有相对位置,从而可以在任意地方调用生成的动态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.h  add.o  main.c  sub.c  sub.h  sub.oubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -shared -o libmymath.so *.o# shared:表示生成共享库格式# 库名规则:lib*.soubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.h  add.o  libmymath.so  main.c  sub.c  sub.h  sub.o

4.2 打包动态库

将 动态库(.so)和 头文件(.h)打包到一个文件夹:

# ---------- Makefile ----------libmymath.so:add.o sub.ogcc -shared -o $@ $^%.o:%.cgcc -fPIC -c $<.PHONY:cleanclean:rm -rf libmymath.so *.o DynamicLib.PHONY:DynamicLibDynamicLib:mkdir DynamicLibcp *.h DynamicLibcp libmymath.so DynamicLib
# ubuntu 20.04 实机演示# 构建动态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.h  main.c  Makefile  sub.c  sub.h  TestStaticLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ makegcc -fPIC -c add.cgcc -fPIC -c sub.cgcc -shared -o libmymath.so add.o sub.oubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.o         main.c    sub.c  sub.oadd.h  libmymath.so  Makefile  sub.h  TestStaticLib# 打包ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make DynamicLib mkdir DynamicLibcp *.h DynamicLibcp libmymath.so DynamicLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ lsadd.c  add.o       libmymath.so  Makefile  sub.h  TestStaticLibadd.h  DynamicLib  main.c        sub.c     sub.o# 查看打包文件ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree DynamicLib/DynamicLib/├── add.h├── libmymath.so└── sub.h0 directories, 3 files

4.3 使用动态库

故技重施,创建 TestDynamicLib 文件夹进行测试:

# 把上面的动态库拿过来,并创建 Makefile 和 test.cubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree TestDynamicLib/TestDynamicLib/├── DynamicLib│   ├── add.h│   ├── libmymath.so│   └── sub.h├── Makefile└── test.c# test.c 与静态库保持一致# Makefileubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cat TestDynamicLib/Makefile test:test.cgcc -o $@ $^ -I ./DynamicLib -L ./DynamicLib -l mymath.PHONY:cleanclean:rm -f test

直接编译试试?

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestDynamicLib/# 编译ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ makegcc -o test test.c -I ./DynamicLib -L ./DynamicLib -l mymathubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ lsDynamicLib  Makefile  test  test.c# 运行ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test ./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

发现报错了,系统找不到我们的动态库,为什么?

上面我们提到过库搜索路径,我们只是在编译时指定了路径,只有编译器知道动态库的位置,编译完成后就没有人知道去哪里找动态库了!所以可执行程序是找不到动态库的!

那为什么静态库没事呢?因为静态库是直接把库链接到可执行文件中,编译完成后把静态库删掉都不会影响可执行文件

动态库不一样,程序每次执行都要去磁盘上搜索动态库的位置,然后链接,由于我们并没有指定动态库的位置,所以找不到!

如何解决?

一个比较简单且没有副作用的方法是修改 LD_LIBRARY_PATH 环境变量,因为我们只是想测试,并不想真的把这个动态库添加到系统中,而环境变量在我们关掉 xshell 后就会重置,所以我们选择这种方法进行测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ pwd/home/ubuntu/Linux/test_4_20/TestDynamicLib# 修改环境变量ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ export LD_LIBRARY_PATH=/home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLib# 查看环境变量ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ echo $LD_LIBRARY_PATH/home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLibubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ lsDynamicLib  Makefile  test  test.c# 执行测试程序ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test add(10, 20) = 30sub(100, 20) = 80# 测试成功

5. 动静态库优先级

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

点击全文阅读


本文链接:http://m.zhangshiyu.com/post/195743.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1