UNIXC01 动态加载

动态加载

  • 对动态库的加载分为自动加载动态加载两种

  • 自动加载

    • 程序在开始执行的时候, 将依赖的动态库文件加载到内存中, 再进行函数的链接, 称为自动加载 (之前讲动态库讲过)
  • 动态加载

    • 程序在执行期间, 需要使用到某个动态库中的文件的时候, 可以向动态链接器发出请求, 请求将动态库文件加载到内存中. 这种行为称为动态加载

1. 动态加载API

  • 动态链接器为动态库加载提供了相应的API(动态链接器也是一个封装好的库, 下面讲库中提供的四个常用的API)
    • dlopen(3) dlclose(3) dlerror(3) dlsym(3)
    • man 3 dlopen 可以查看函数细节

1. dlopen(3)

#include <dlfcn.h>
void *dlopen(const char *filname, int flags);
  • 功能: 将动态库加载到内存
  • 参数:
    • filename: 共享路径. 如果只给定文件的名字, 按照动态链接器的搜索路径去找. LD_LIBRARY_PATH指定的路径, 或者默认路径下(/usr/lib | /lib)找
    • flags: 加载方式, 可取以下值
    • RTLD_LAZY - 延迟加载, 使用共享库中的符号(如调用库中的函数)时才加载
    • RTLD_NOW - 立即加载, 函数返回的时候, 已经加载到内存
  • 返回值:
    • 成功: 返回动态库加载到内存的地址
    • 失败: NULL 可以使用delerror(3)函数诊断错误
  • 注意:
    • 动态链接器的API需要使用到dl动态库文件, 所以链接的时候要用 -ldl

2. dlclose(3)

#include <dlfcn.h>
int dlclose(void *handle);
  • 功能: 关闭动态库, 仅仅使动态库的引用计数减一, 并不一定从内存中移除, 只有引用计数为0时候, 才从内存中移除.
  • 参数:
    • handle: 动态库加载到内存地址( dlopen(3)的返回值 )
  • 返回值:
    • 成功: 0
    • 失败: 非o 可以使用dlerror(3)诊断错误的原因

3. dlerror(3)

#include <dlfcn.h>
char *dlerror(void);
  • 功能: 获取dlopen API的错误信息
  • 返回值:
    • 有错误: 返回错误原因字符串的首地址
    • 没错误: NULL

4. dlsym(3)

在这里插入图片描述

  • dlsym(3) 在加载到内存中动态库中,找到函数的地址
  • 符号包括,全局变量,静态局部变量,函数名等

2. 代码实例

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;
}

dynamic.c(动态加载模块)

#include <stdio.h>
#include <dlfcn.h>

//定义一个函数指针, int (*fun_t)(int, int);
// 加上typedef, fun_t 就从函数指针变成了成函数指针类型pfun_t
typedef int (*pfun_t)(int, int);

// 通过命令行的第一个参数传递要动态加载的动态库的名字
int main(int argc, char *argv[]){
    void *handle = dlopen(argv[1],RTLD_NOW);
    if(handle==NULL){
        printf("load failed... %s\n", dlerror());
        return -1;
    }
    printf("load sucess...\n");

    void *f = dlsym(handle, "t_mul");
    if(f==NULL){
        printf("dlsym: %s\n", dlerror());
        return -1;
    }
    // 不能直接用f, 因为f是 void*(无类型指针不能直接使用)
    // 这里应该做了将void*  到 int* 的类型隐式转换?
    pfun_t fn=f; 
    printf("3 * 5 = %d\n", fn(3, 5));

    dlclose(handle);
    return 0;
}
  • 操作步骤
$ gcc -shared -o libp_math.so add.o mul.o
$ sudo mv libp_math.so /lib/.
$ gcc dynamic.c -c
# 动态链接器的API需要使用到dl动态库文件, 链接的时候要用 -ldl
$ gcc dynamic.o -ldl 
# 上面两边合起来是: gcc dynamic.c -ldl

$ a.out libp_math.so
load sucess...
3 * 5 = 15

# 没有 t_mul 函数的标识符, 只有4个库函数(自动加载)
# 链接时-ldl 动态链接, dynamic.c 中只用到dl中4个函数, 这里也就显示4个函数名
$ nm a.out
....
0000000000400690 t deregister_tm_clones
                 U dlclose@@GLIBC_2.2.5
                 U dlerror@@GLIBC_2.2.5
                 U dlopen@@GLIBC_2.2.5
                 U dlsym@@GLIBC_2.2.5
....
0000000000400756 T main
                 U printf@@GLIBC_2.2.5
                 U puts@@GLIBC_2.2.5
00000000004006d0 t register_tm_clones
0000000000400660 T _start
0000000000601060 D __TMC_END__

# 而且 a.out 也并不依赖于 libp_math.so, 依赖是 libdl.so.2(因为dl库在这里属于自动加载)
# a.out 加载到内存执行的时候,将依赖的动态库ld加载到内存, 再进行函数的链接, 这属于自动加载
# 而使用ld库中的函数去加载 libp_math.so动态库中的函数到内存中的这种行为称为动态加载
$ ldd a.out 
        linux-vdso.so.1 =>  (0x00007fff6b8c2000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fea1e539000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fea1e16f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fea1e73d000)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值