主程序调用动态库有两种方式,即隐式调用和显式调用。

隐式调用就是共享方式,程序一开始运行就调进去。在链接时候用如下的方式链接动态库:gcc -o main main.o -L./lib -ltest(就像链接像静态库的一样)

显示调用就是在程序中用系统调用把动态库加载进来,用系统调用:dlopen、dlsym、dlerror、dlclose函数,那样在编译链接时候,就不用加上:-L./lib -ltest了。不过因为要使用dl*系列函数,需要在编译链接时要加上 -ldl 。

动态库可以直接调用主程序中定义的函数,其实现机制是:动态链接器在完成基本自举后, 动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表中, 我们可以称它为全局符号表(Global Symbol Table)…..当一个新的share object被装载进来的时侯, 它的符号表会被合并到全局符号表中。因此,动态库加载后其调用的主程序中的函数就是从全局符号表中找到该函数的地址并调用的。

下面通过实例进行说明。

1、隐式调用动态库

//hi.c文件,编译为libhi.so
extern void hi_out();void hello() { hi_out();
}
gcc -shared -fPIC -o libhi.so hi.c
//main.c文件,编译为main
#include <stdio.h>void hi_out() { printf("hi out.\n");
}extern void hello();  //语句1
int main() {hello();  //语句2return 0;
}
gcc main.c -L. -lhi -Wl,-rpath=. -o main

隐式调用方式,主函数可以直接调用动态库的函数,反之动态库也可以直接调用主程序中的函数。

需要注意的是,我们知道,动态库是在程序运行时链接动态库的,在运行主程序时,需要主程序能顺利找到动态库。在编译时加入-Wl,-rpath=. 这种方式是在编译时就指定了程序需要去找的路径,此外还有其他的方式,可以参考https://blog.csdn.net/qq_28584889/article/details/120070042

2、显式调用动态库

hi.c文件保持不变,同样的方式编译为libhi.so

修改一下main.c如下:

#include <dlfcn.h>
#include <stdio.h>void hi_out() { printf("hi out.\n"); }// extern void hello();  //语句1
int main() {const char* pstr = "./libhi.so";void* library = dlopen(pstr, RTLD_NOW);  //如果用RTLD_LAZY,则会在语句4报错if (!library) {printf("load dlopen(%s): %s\n", pstr, dlerror());  //语句3,RTLD_NOW模式在这报错return -1;}void (*pfun)();pfun = (void (*)())dlsym(library, "hello");if (pfun) {pfun();  //语句4}return 0;
}
gcc main.c -ldl -o main

编译之后执行./main, 发现报错:

load dlopen(/data1/home/menglonglu/hold_proj/test/libhi.so): /data1/home/menglonglu/hold_proj/test/libhi.so: undefined symbol: hi_out

原因是: ld在生成可执行文件时, 默认只导出被其他动态库使用的符号. 因为是使用dlopen去动态加载libhi.so, 那么链接时ld并不知道可执行文件中的hi_out会被外部引用, 也就不会导出hi_out到动态符号表去. 当dlopen打开libhi.so时, 动态链接器在全局符号表中找不到hi_out符号, 理所当然就报错了。

要解决这个问题只要给链接器加上参数-E将主程序中所有全局符号放到动态符号表中即可, 由于生成可执行文件一般都是gcc直接生成, 因此可以使用gcc -Wl,-E来将-E参数传给ld来完成创建一个可以被动态链接的可执行文件。

编译主程序: gcc main.c -ldl -Wl,-E -o main 编译成可执行文件,运行ok。

或者把-Wl,-E 换成 -rdynamic,都可以实现同样的效果。

-rdynamic 是一个 连接选项 ,它将指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号)都添加到动态符号表(即.dynsym表)里,以便那些通过 dlopen() 或 backtrace() (这一系列函数使用.dynsym表内符号)这样的函数使用。具体可参考https://blog.csdn.net/adam040606/article/details/53097650

3、拓展:使用cmake编译

我发现当使用cmake编译例2中的主程序时,应当是默认把所有符号都加载到全局符号表里了。

我们不加任何选项的,创建一个 CMakeList.txt并使用cmake编译。

cmake_minimum_required(VERSION VERSION 2.8.12)
project(test)add_executable(main main.c)target_link_libraries(main -ldl)

然后cmake . 生成Makefile,make编译成main执行文件,运行ok。

参考链接:https://blog.csdn.net/mw_nice/article/details/108253409

主程序调用动态库的两种方式,动态库调用主程序的函数相关推荐

  1. 动态连接库的两种方式

    动态连接库的两种方式? 答案:调用一个DLL中的函数有两种方法: 1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数,使得他们就像本地函数一样.这需 ...

  2. android asynctask源码分析,Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)...

    本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,noti ...

  3. spring中AOP动态代理的两种方式

    AOP动态代理的两种方式 Spring AOP动态代理的方式(spring的AOP默认是JDK Proxy) 浅谈这两种动态代理 JDK的动态代理,需要有实现接口 动态代理--JDK Proxy ⚫ ...

  4. Vue实现动态路由的两种方式总结

    实现动态路由有两种方式,一种是后端返回什么,前端就展示什么,另一种是后端只返回角色,前端根据角色拼接数据信息展示.相比第一种方式,第二种方式在企业中更常用. 第一种方式: (一)后端需返回类似Vue- ...

  5. mysql不停止重启服务器_不停止MySQL服务增加从库的两种方式

    现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库.前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作. ...

  6. mysql增加从库_不停止MySQL服务增加从库的两种方式 (装载)

    现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库.前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作. ...

  7. mysql 停从库_不停止 MySQL 服务增加从库的两种方式

    现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库.前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作. ...

  8. 不停止MySQL服务增加从库的两种方式

    本文出自51CTO博客博主李振良的技术博客,如有任何问题请进入博主页面互动讨论. 博文地址:http://lizhenliang.blog.51cto.com/7876557/1669829 现在生产 ...

  9. Http调用第三方接口的两种方式实例《超详细!!!》***

    Http调用第三方接口的两种方式<超详细!!!>* 最近在公司做一些调用第三方接口的工作,查阅了一部分的资料和向前辈以及朋友请教,完成了第三方接口的调用,其实主要是通过第三方提供的文档,完 ...

最新文章

  1. sizebox模型下载_css 盒模型、box-sizing 学习笔记
  2. 工业大数据的真正意义和价值
  3. Js操作表格-对表格单元格的添加删除修改
  4. javaweb----DAO模型设计
  5. oracle数据库改名步骤,oracle 11g2 数据库改名详细解释 oracle数据库改名详解 oracle database 改名详解 dbname...
  6. autoconf常用宏
  7. 【转】Dicom格式文件解析器!!!!!!!
  8. charles 简单使用
  9. mysql数据库中数据类型的长度
  10. 程序运行 栈帧分析 以及 修改栈帧中数据以及函数地址
  11. Pytorch还是TensorFlow?顶会带你览趋势
  12. SMTP协议初探(二)----linux下c编程实现发邮件
  13. flink sink jdbc没有数据_一套 SQL 搞定数据仓库?Flink 有了新尝试
  14. Tensorflow的最佳实践
  15. 机器学习的1000+篇文章总结
  16. Boxy SVG 3.24 特别版 Mac 强大的矢量图编辑软件
  17. Linux 内存管理 | 虚拟内存管理:虚拟内存空间、虚拟内存分配
  18. 计算机专业必读哪些经典书籍?
  19. 【嵌入式软件开发】之面试常识(一)
  20. 《利用python进行数据分析》第二版 第13章-Python建模库介 学习笔记

热门文章

  1. 安装麒麟操作系统及达梦数据库DM8详细步骤教程
  2. linux upstart脚本,ubuntu upstart简单说明
  3. h5获取pdf文件实现预览
  4. onscroll事件与onscrollTop
  5. 计算机教育研究生考什么,计算机在职研究生考什么科目
  6. vscode全网最详细使用教程
  7. mysql tpcc load_mysql 数据库TPCC测试
  8. HTML5 SVG可爱笑脸动画
  9. 2017架构年度参考,“优秀”架构背后的反思与探索
  10. 载噪比C/N和信噪比S/N