[学习笔记]达内纪录片day20 动态加载与错误处理

    选择打赏方式

一、动态加载
在程序运行的过程中,需要使用到动态库里的函数的时候,将动态库加载到内存中。
程序员向系统提出申请
动态链接器本身就是一个软件
动态向外提供了接口。dlopen API
这种加载就是动态加载
在程序中动态加载共享库需要调用一组特殊的函数,它们被声明于<dlfcn.h>,编译使用该库的时候需要加上-ldl参数


加载共享库

将共享库载入内存并获得其访问句柄
void *dlopen(const char *filename,int flag)

参数:
-filename:共享库路径,若只给文件名,则根据LD_LIBRARY_PATH环境变量搜索
-flag:加载方式,可取以下值
    RTLD_LAZY-延迟加载,使用共享库中的符号(如调用库中的函数)时才加载
    RTLD_NOW-立即加载

返回值:成功返回共享库句柄,失败返回NULL

说明:共享库访问句柄唯一地标识了系统内核所维护的共享库对象,将作为后续函数调用的参数


获取函数地址

从指定共享库中获取与给定函数名对应的函数入口地址

void *dlsym(void *handle,const char *symbol)

参数:
-handle:共享库访问句柄1
-symbo/:函数名

返回值:成功返回函数地址,失败返回NULL

说明:所返回的函数指针是void*类型,需要造型为实际函数指针类型才能调用


卸载共享库

从内存中卸载共享库
int dlclose(void *handle)

参数:
-handle:共享库访问句柄

返回值:成功返回0,失败返回非0

说明:

·所卸载的共享库未必真的会从内存中立即消失,因为其它程序可能还需要使用该库
·只有所有使用该库的程序都显式或隐式地卸载了该库,该库所占用的内存空间才会真正得到释放
·无论所卸载的共享库是否真正被释放,传递给dlclose函数的句柄参数都会在该函数成功返回后立即失效


获取错误信息

获取在加载、使用和卸载共享库过程中所发生的错误

char* dlerror(void)

返回值:有错误则返回指向错误信息字符串的指针,否则返回NULL


演示源码:

动态链接库 libp_math.so

头文件

t_math.h

#ifndef T_MATH_H_
#define T_MATH_H_
int t_add(int,int);
int t_sub(int,int);
int t_mul(int,int);
int t_div(int,int);
#endif


dynamic.c(动态加载demo)


//通过命令行第一个参数传递要加载的动态库文件的名字
#include <stdio.h>
#include <dlfcn.h>
typedef int (*m_t)(int,int);

int main(int argc,char *argv[]){
    //打开动态库文件
    void *h=dlopen(argv[1],RTLD_NOW);
    if(!h){
        printf("dlopen failed...%s\n",dlerror());
        return -1;
    }
    //加载成功
    printf("dlopen success...\n");
    //在动态库中查找函数“t_sub”的入口地址
    void *f=dlsym(h,"t_sub");
    if(!f){
        printf("%s\n",dlerror());
        return -1;
    }
    //f是一个指针类型的变量,
    //f变量的空间里存放了t_sub函数的入口地址
    m_t m=(m_t)f; 
    printf("8-6=%d\n",m(8,6));

    //关闭动态库
    dlclose(h);
    return 0;
}


运行结果:

TIM截图20190728182609.png


二、辅助工具

反汇编

显示二进制模块的反汇编信息

objdump -S a.out


去除冗余信息

去除目标文件、可执行文件、静态库和共享库中的符号表、调试信息等

strip a.out


查看共享库依赖

查看可执行文件或共享库所依赖的共享库文件

ldd a.out


配置管理共享库

用专门的配置文件管理共享库的搜索路径
-事先将共享库的路径信息写入/etc/ld.so.conf配置文件中
-执行ldconfig命令,将/etc/ld.so.conf配置文件转换为/etc/ld.so.cache缓冲文件,并将后者加载到系统内存中,借以提高共享库的搜索和加载速度

-每次系统启动时都会自动执行ldconfig命令
-如果修改了共享库配置文件/etc/ld.so.conf,则需要手动执行ldconfig命令,更新缓冲文件并重新加载到系统内存


三、错误处理

错误号:

系统预定义的整型全局变量errno中存储了最近一次系统调用的错误编号
头文件errno.h中包含对errno全局变量的外部声明和各种错误号的宏定义


错误信息:

将整数形式的错误号转换为有意义的字符串
#include<string.h>
char* strerror(int errnum)

参数:

-errnum:错误号

返回值:与参数错误号对应的描述字符串指针


将上一个函数发生错误的原因输出到标准设备(stderr)

#include<stdio.h>

void perror(const char* str)

参数:

-str:显示在错误信息之前的自定义字符串


演示源码


#include <stdio.h>
int main(int argc,char *argv[]){
    FILE *fp=fopen(argv[1],"r");
    if(fp==NULL){
	//错误号获取错误描述字符串
	printf("fopen failed...%d\n",errno);
        errno=-1;
        printf("fopen failed...%s\n",strerror(errno));
	//直接输出自定义字符串+错误描述字符串
        perror("fopen");        
        return -1;
    }
    printf("fopen file %s success...\n",argv[1]);
    fclose(fp);
    return 0;
}


头文件封装

t_stdio.h

#ifndef T_STDIO_H_
#define T_STDIO_H_
#include <stdio.h>
#define E_MSG(STR,VAL) do{perror(STR);return (VAL);}while(0)
#endif


演示源码

#include <t_stdio.h>
int main(int argc,char *argv[]){
    FILE *fp=fopen(argv[1],"r");
    if(fp==NULL)E_MSG("fopen",-1);
    printf("fopen file %s success...\n",argv[1]);
    fclose(fp);
    return 0;
}


注意:成功不会修改错误号,所以错误号不能作为函数是否执行成功的依据,错误号全局变量并不是线程安全的!


THE END!


版权声明:若无特殊注明,本文皆为《 8964CN 》原创,转载请保留文章出处。
本文链接:[学习笔记]达内纪录片day20 动态加载与错误处理 http://www.8964cn.net/?post=81
正文到此结束

热门推荐

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 害羞 调皮 鄙视 示爱 大哭 开心 偷笑 嘘 奸笑 委屈 抱抱 愤怒 思考 日了狗 胜利 不高兴 阴险 乖 酷 滑稽

评论信息框

吃奶的力气提交吐槽中...


既然没有吐槽,那就赶紧抢沙发吧!