小秦哥 发表于 2024-6-13 00:35:22

【Linux】动态库与静态库

https://img-blog.csdnimg.cn/direct/02227bc29471432f8b1b7b2d879ec59d.gif#pic_center
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 -l
total 20
-rw-rw-r-- 1 ubuntu ubuntu61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu65 Apr 20 20:14 sub.h

# 生成 .o 文件,准备打包为静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c add.c -o add.o
ubuntu@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 Jan1 08:00 1970 add.o
rw-r--r-- 0/0   1376 Jan1 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$ ls
add.cadd.hadd.oa.outlibmymath.amain.csub.csub.hsub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out
add(10, 20) = 30
sub(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) = 30
sub(100, 20) = 80
2.2 打包静态库

如果想把这个静态库给别人利用,我们需要将 库(.a)和 头文件(.h)一起打包;如许别人在看到头文件的时候,就知道我们的库中封装了哪些方法。
利用 Makefile 进行自动化编译:
# ---------- Makefile ----------
libmymath.a:sub.o add.o
        ar -rc $@ $^

%.o:%.c
        gcc -c $<

.PHONY:clean
clean:
        rm -rf *.o output libmymath.a

# 将相关文件打包到 output 文件夹
.PHONY:output
output:
        mkdir output
        cp -rf *.h output
        cp libmymath.a output
# ubuntu 20.04 实机演示

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 24
-rw-rw-r-- 1 ubuntu ubuntu61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu65 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 ubuntu61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu65 Apr 20 20:14 sub.h

# 构建静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
gcc -c sub.c
gcc -c add.c
ar -rc libmymath.a sub.o add.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 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 ubuntu243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu188 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 output
cp -rf *.h output
cp libmymath.a output
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 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 ubuntu243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu188 Apr 21 13:38 Makefile
drwxrwxr-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

# 查看 output
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree output/
output/
├── add.h
├── libmymath.a
└── sub.h

0 directories, 3 files
2.3 利用静态库

我们已经将静态库以及相关头文件打包到了 output 文件夹,如今我们新建一个 TestStaticLib 文件夹进行测试:
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mkdir TestStaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 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 ubuntu243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu188 Apr 21 13:38 Makefile
drwxrwxr-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
drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:56 TestStaticLib

# 将 output 移动到 TestStaticLib 文件夹,并改名为 StaticLib
ubuntu@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 StaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls
StaticLib
在 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$ ls
StaticLibtest.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ touch Makefile

# 编写 Makefile
ubuntu:~/Linux/test_4_20/TestStaticLib$ vim Makefile
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ cat Makefile
test:test.c
        gcc -o $@ $^ -I ./StaticLib -L ./StaticLib -l mymath
        # -I:指定头文件所在目录
        # -L:指定静态库所在目录
        # -l:指定库名

.PHONY:clean
clean:
        rm -f test
测试:
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ make
gcc -o test test.c -I ./StaticLib -L ./StaticLib -l mymath
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls -l
total 32
-rw-rw-r-- 1 ubuntu ubuntu    99 Apr 21 14:21 Makefile
drwxrwxr-x 2 ubuntu ubuntu4096 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.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ./test
add(10, 20) = 30
sub(100, 20) = 80
3. 库搜刮路径



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

[*]/ user / lib
[*]/ user / local / lib

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

4.1 生成动态库

利用下令手动生成静态库:
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.cadd.hmain.csub.csub.h
ubuntu@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$ ls
add.cadd.hadd.omain.csub.csub.hsub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -shared -o libmymath.so *.o
# shared:表示生成共享库格式
# 库名规则:lib*.so

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.cadd.hadd.olibmymath.somain.csub.csub.hsub.o
4.2 打包动态库

将 动态库(.so)和 头文件(.h)打包到一个文件夹:
# ---------- Makefile ----------
libmymath.so:add.o sub.o
        gcc -shared -o $@ $^

%.o:%.c
        gcc -fPIC -c $<

.PHONY:clean
clean:
        rm -rf libmymath.so *.o DynamicLib

.PHONY:DynamicLib
DynamicLib:
        mkdir DynamicLib
        cp *.h DynamicLib
        cp libmymath.so DynamicLib
# ubuntu 20.04 实机演示

# 构建动态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.cadd.hmain.cMakefilesub.csub.hTestStaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
gcc -fPIC -c add.c
gcc -fPIC -c sub.c
gcc -shared -o libmymath.so add.o sub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.cadd.o         main.c    sub.csub.o
add.hlibmymath.soMakefilesub.hTestStaticLib

# 打包
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make DynamicLib
mkdir DynamicLib
cp *.h DynamicLib
cp libmymath.so DynamicLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.cadd.o       libmymath.soMakefilesub.hTestStaticLib
add.hDynamicLibmain.c      sub.c   sub.o

# 查看打包文件
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree DynamicLib/
DynamicLib/
├── add.h
├── libmymath.so
└── sub.h

0 directories, 3 files
4.3 利用动态库

故技重施,创建 TestDynamicLib 文件夹进行测试:
# 把上面的动态库拿过来,并创建 Makefile 和 test.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree TestDynamicLib/
TestDynamicLib/
├── DynamicLib
│   ├── add.h
│   ├── libmymath.so
│   └── sub.h
├── Makefile
└── test.c
# test.c 与静态库保持一致

# Makefile
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cat TestDynamicLib/Makefile
test:test.c
        gcc -o $@ $^ -I ./DynamicLib -L ./DynamicLib -l mymath

.PHONY:clean
clean:
        rm -f test
直接编译试试?
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestDynamicLib/

# 编译
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ make
gcc -o test test.c -I ./DynamicLib -L ./DynamicLib -l mymath
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
DynamicLibMakefiletesttest.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/DynamicLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
DynamicLibMakefiletesttest.c

# 执行测试程序
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test
add(10, 20) = 30
sub(100, 20) = 80

# 测试成功
5. 动静态库优先级



[*]如果我们同时提供动态库和静态库,gcc 默认利用的是动态库;
[*]如果我们非要静态链接,我们必须利用 static 选项;
[*]如果我们只提供静态库,那我们的可实行程序也没办法,只能对该库进行静态链接,但是程序不肯定团体是静态链接的。
[*]如果我们只提供动态库,默认只能动态链接,如果非要静态链接,会发生链接报错!
   END
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux】动态库与静态库