UNIXC01 动态库的制作和使用

动态库的制作和使用

1. 制作步骤

  • 将所有要加入库的源文件编译为目标文件(与位置无关的目标文件(后面讲内存会提到))
    • gcc -c -fPIC add.c mul.c
  • 将第一步生成的目标文件打包到动态库文件中
    • gcc -shared -o libp_math.so add.o mul.o
  • 使用动态链接库生成可执行文件
    • 在解决依赖(把动态库文件放到/usr/lib 或者 /lib下)的前提下直接 gcc main.o -lp_math
    • 如果只是改变了 LD_LIBRARY_PATH, 链接目标文件时要加库路径 gcc main.o -L. -lp_math
  • 不管那种方式解决依赖, a.out 都可以正常执行可执行文件, 只是以 第一种方式解决后, 链接起来不用再加-L库文件路径

2. 解决动态库依赖

  • 查看可执行文件对动态库的依赖
    • ldd 可执行文件
  • 告知动态链接器如何找到动态库
    • LD_LIBRARY_PATH
    • /usr/lib 或者 /lib

3. 代码实例

p_math.h (定义各个模块间的接口)

// 如果没有定义了这个宏, 就定义这个宏,紧接着进行函数声明, 最后endif. 如果定义过, 就直接跳到endif
#ifndef P_MATH_H_ //为了避免头文件重复包含
#define P_MATH_H_ 
//函数声明
int t_add(int, int);
int t_sub(int, int);
int t_mul(int, int);
int t_div(int, int);

#endif

add.c (加法模块)

#include "p_math.h"
int t_add(int x, int y){
    return x + y;
}
int t_sub(int x, int y){
    return x - y;
}

mul.c (乘法模块)

#include "p_math.h"
int t_mul(int x, int y) {
    return x * y;
}

int t_div(int x, int y) {
    return x / y;
}

main.c

#include "p_math.h"
#include <stdio.h>

int main(void){
    int val_x = 6, val_y = 2;
    printf("%d + %d = %d\n", val_x, val_y, t_add(val_x, val_y));
    printf("%d * %d = %d\n", val_x, val_y, t_mul(val_x, val_y));
}
  • 制作步骤
# 1. 将所有要加入库的源文件编译为目标文件(与位置无关的目标文件)
$ gcc -c -fPIC add.c mul.c
# 2. 将第一步生成的目标文件打包到动态库文件中
$ gcc -shared -o libp_math.so add.o  mul.o
# 3. 使用动态链接库生成可执行文件
$ gcc main.o -L. -lpmath

# a.out 和动态库是两个模块, 要执行a.out, 必须把动态库加载到内存当中
# 否则 a.out 找不到依赖的动态库
$ a.out 
a.out: error while loading shared libraries: libp_math.so: cannot open shared object file: No such file or directory

# ldd可以查看可执行程序依赖的动态库, libp_math.so 找不到
$ ldd a.out 
        linux-vdso.so.1 =>  (0x00007fff7b390000)
        libp_math.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbb49bd0000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbb49f9a000)

# 两种常用解决动态库依赖的方法
#1. 通过 LD_LIBRARY_PATH 环境变量告诉连接器去哪里找依赖的动态库
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
$ ldd a.out         linux-vdso.so.1 =>  (0x00007fffd30a3000)
        libp_math.so (0x00007fed51b12000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fed51748000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fed51d14000)
$ a.out
6 + 2 = 8

# 2. 把动态库文件放到链接器默认查找路径下
# 链接器(动态, 静态)默认路径是 /lib|/usr/lib, 把动态库移动到这下面
$ sudo mv libp_math.so /lib/.
$ ldd a.out 
        linux-vdso.so.1 =>  (0x00007ffc3e36d000)
        libp_math.so => /lib/libp_math.so (0x00007f674cd0f000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f674c945000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f674cf11000)
$ a.out
6 + 2 = 8

# 所以如果在链接main.o以前就把 libp_math.so 放到/lib/. 下, 
# 链接的时候直接gcc main.o 就可以, 不用在写-L 直接写 -l 就可
$ rm a.out
$ gcc main.o -lp_math
$ a.out
6 + 2 = 8

4. 动态库优点

在这里插入图片描述
动态库和静态库区别:

  • 可执行程序链接好以后不再依赖静态库
  • 可执行程序和动态库是两个独立模块, 前者依赖后者.

所以可以直接更新动态库,不更新可执行程序
把 add.c d 代码改为

#include "p_math.h"

int t_add(int x, int y){
    return x + y + 1;
}
int t_sub(int x, int y){
    return x - y;
}
$ gcc -c -fPIC add.c
$ gcc -shared -o libp_math.so add.o mul.o
$ sudo mv libp_math.so /lib/.
$ gcc main.o -lp_math
$ a.out
6 + 2 = 9

$ nm a.out
# 静态库会把add模块的所以函数都考进来 T t_add T t_sub 地址确定
# 而动态库只会把使用到那个模块的函数标记过来 U t_add 地址不确定
....
00000000004006d6 T main
                 U printf@@GLIBC_2.2.5
0000000000400650 t register_tm_clones
00000000004005e0 T _start
                 U t_add
0000000000601040 D __TMC_END__
....

5. 动态链接器

  • 自动加载: 一种是程序加载到内存执行的时候,将依赖的动态文件加载到内存, 再进行函数的链接, 也就是以上
  • 动态加载: 另一种是程序运行的过程中, 需要使用到动态库中的函数时候(后面讲到动态加载时说)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值