从一个logger引发的lib和dll探讨
来源:互联网 发布:51单片机郭天祥 编辑:程序博客网 时间:2024/06/06 03:02
一. 问题来由
项目写了个logger,本来是代码的,大家单独包含都可以使用,但是后来项目整合,每个人的部分打成lib,而前端将logger打包一起编译成lib,后台按道理应该是不用包含 .cpp 文件也可以用到logger,因为如果后台也包含并编译,应该会和前端的编译单元中的 logger 重定义。
但是奇怪的是,非但不重定义,反而这边获取不到 io 流 (流是静态变量),匪夷所思,最后是采用命名空间解决了。
虽然那个问题没有完美解决,但是我决定好好搞一下 lib 和 dll ,因为仅就 logger 来说:
好处1 这个项目中,日志模块是完全独立的模块,从设计的角度来看,本来就应该独立开来打包,然后大家就可以一起用这个包,实现了松耦合;
好处2 logger 打成 lib 或者 dll,大家就可以像使用一些api一样松松使用,不仅用于这个项目,以后实验室的项目都可以使用。
另外,也可以彻底搞一下动态库和静态库。
二. 从最简单的定义说起
最简单的定义,也是最具有可信性的:
lib 和 dll
一个 lib 文件是obj文件的集合。
当然,其中还夹杂着其他一些辅助信息,目的是为了让编译器能够准确找到对应的 obj 文件。我们可以通过tlib.exe(在tc2.0下的根目录)来对lib文件进行操作
dll 是运行时装载的函数实现代码,个人认为是函数实现代码段汇编对应的二进制码。
静态编译与动态编译
注: 本人对于静态编译和动态编译的概念几经查证,尚无定论,先留白在此,而后查证后更新。
如下解释只是个人理解。
动态编译: 可执行文件需要附带一个的动态链接库,动态链接库在运行时加载
静态编译: 将附加的静态链接库链接进最后的可执行文件,是编译期所做的事情
三. 静态链接库版本 logger
本人编译环境为: win7 vs2013,静态链接库为lib
1 生成 lib
我希望将 logger 打成一个 lib,供外部链接调用。理清包含关系如下:
(1) logger.h 包含 mutex.h 和一些其他头文件
(2) mutex.h 包含 pthread.h,并在代码中加入#pragma comment(lib, "pthreadVC2.lib")
,用到第三方的函数
即: logger.h —> mutex.h —> pthread.h
而 pthread 提供 pthread.h + pthread 相关 lib + pthread 相关 dll
正片开始:
我发现,如果选择生成 lib,编译选项里面并不能添加 “附加依赖项”,所以我大胆猜测 代码中编译制导语句链接 pthread lib 那句话在生成 lib 时没有用。
注释编译制导语句,生成 lib 成功~~
这也就是说,生成 lib 的时候,并不会进行链接,自然就不会进行重定义和外部定义的检查,只会进行将当前项目的编译单元生成中间文件 (这里是 .obj 文件) 打包,即使在代码中指明需要链接外部 lib。
即 lib 不能链接外部 lib~~
不仅是静态 lib 不允许链接外部 lib,看起来动态 lib 好像可以链接,其实也不是,动态 lib 中只是 dll 暴露接口的函数入口地址,并不包含任何函数实现。
个人认为编译器这么做的目的,是为了防止很多 lib 重复定义同一个外部 lib。而且生成 lib 的确不应该做链接,因为生成一个 lib 的最终职能是为了让包含它的可执行程序跑起来,而仅仅生成 lib 时,都不完整,立马就链接是没有意义的。
2 外部使用 lib
需包含上一步生成的 lib,使用#pragma comment(lib, "logger.lib")
完成
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
编译器配置如下:
特别需要注意的是:
因为上一步生成 lib 时,没有包含 pthread 的 lib 文件,在最后生成可执行文件的时候,必须:
(1) 包含 lib 所在文件夹为库文件夹
(2) 在包含的用到 pthread lib 文件的头文件处 (这里是 mutex.h),使用#pragma comment(lib, "pthreadVC2.lib")
或项目属性中设置附加依赖项
(3) pthread dll 放到可执行程序目录,或者放到环境变量中
然后就可以正常使用了。
四. 动态库链接库版本 logger
因为是 win 平台,所以生成动态库是 dll
1 生成 dll
dll 是实现的二进制码,运行时装载,自然在项目属性中有了 “附加依赖项” 的选项,这意味着生成 dll 时,是需要链接的,dll 中包含外部 lib 的实现。
于是这里需要包含,pthread lib 目录,并使用#pragma comment(lib, "pthreadVC2.lib")
或项目属性中设置附加依赖项
另外由于 dll 的特殊性,dll 需要暴露在黑盒外的函数接口有特殊写法,详见代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
用 depends.exe 查看结果如下,可以清晰的看到,dll 是给出暴露接口的函数入口地址
生成以后,会在输出目录下生成一个 dll 和一个 lib,这个lib 应该就是所谓的动态 lib,是对 dll 文件的索引。
动态库生成的那个lib一般管它叫“导入库”,这样的lib在大多数情况下要比静态库的lib小,里面不包含涉及到的函数的具体代码,里面只包含“这个函数在什么dll里面叫什么名字”这样的信息。
2 外部使用 dll
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
如果细心可以发现,我上面代码中,将 #include "loggerAPI.h"
注释掉了,而改成了包含很多其他的头文件和枚举量申明,原因就是,使用 dll 其实没有必要包含实现 dll 的全部头文件,只要函数指针类型正确,所有变量类型无误,即可获得函数地址,读者可自行试验。
dll 的两边只要函数签名完全一致,即可
所以这边既不用包含 pthread.h,也不用包含 pthread lib库,不过 pthread dll 还是需要的,因为 logger.dll 中链接的 pthread lib 库,依赖 pthread dll
如果是动态 lib + dll 的组合,不用动态解析 dll,直接使用 头文件中 extern C 的函数即可
五. 驳斥
今天在看一篇博文的时候,发现其中很大问题,关键是这篇文章还高居搜索排行榜首
http://blog.csdn.net/wuan584974722/article/details/7953213
问题1: 动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明
相当于一个h文件,已经有头文件了,何须再有头文件。另外是对 dll 的声明,声明已在头文件中,这种说法就不对了,应该是保存了函数入口地址,相当与解析了dll~~
这里就再延伸一下,动态 lib 和直接使用 dll 有什么优缺点呢?
动态 lib: 省去使用者需要解析 dll 的麻烦,直接使用 lib,出现问题可以在编译器查出来。不过如果 dll 中函数原形改变,当然函数入口地址就改变了,这样使用 lib 就重新编译。
直接解析 dll: 如果函数原型改变,也需要重新写函数指针,重编译。
问题2: 静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应动态链接库(.so)中的部分提取出来
链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
网上看到过有的博客可以脱离第三方 dll,不过都是 MFC,
http://www.cnblogs.com/jifeng/archive/2011/06/24/2088872.html,这篇博客里面提到:
1.项目 -> 配置属性->常规->MFC的使用 :在静态库中使用MFC
2.项目 -> 配置属性->C/C++->代码生成->运行库 :选择/MT
减少对 dll 依赖,应该是使用了 MFC 库,可能 MFC 库有特殊的方法(比如编译器偷偷链接到了相应静态库),完成了这个工作,因为 dll 是运行时动态加载到内存的,而想编译时抽取这是不可能的事。
想想也是,如果 dll 可以做到抽取,exe 也必然可以了,那怎么可能,不然 exe 打包还如何保护代码权益
证据有:https://stackoverflow.com/questions/725472/static-link-of-shared-library-function-in-gcc
第三方解决方案可以做到,但是也不是所谓的编译期抽取
http://bbs.csdn.net/topics/300205804
另附vczh群聊天记录:
所以不得不又一次说,那篇博客的博主是一本正经的胡说了~~
小结
网上找了一些资料,越来越觉得这些底层的知识,需要看专业书籍,正因为网上的资料良莠不齐,我们甄选的时候更需要带批判的眼光看问题
附录
源代码地址:https://github.com/billhhh/logger
参考资料
[1] http://blog.csdn.net/lushuner/article/details/25048465
[2] http://bbs.csdn.net/topics/390177392
[3] https://www.cppfans.org/1394.html
转载自:http://blog.csdn.net/Scythe666/article/details/51278638
- 从一个logger引发的lib和dll探讨
- 从一个logger引发的lib和dll探讨
- 一个debug lib库和libcmt.lib引发的血案
- 一个lib和dll的例子 来自MSDN
- dll和lib的区别
- lib和dll的区别
- dll和lib的区别
- lib和DLL 的关系
- dll和lib的区别
- lib和dll的关系
- dll和lib的区别
- lib和dll的区别
- lib和dll的关系
- dll和lib的区别
- lib和dll的区别
- lib和dll的关系
- lib和dll的关系
- .lib和.dll的知识
- 金融危机可能即将到来
- oracle使用dblink跨库查询的例子
- 寻找Windows下MySQL的错误日志
- Jenkins 持续集成构建
- OPEN(SAP) UI5 学习入门系列之一:扫盲与热身(上)
- 从一个logger引发的lib和dll探讨
- java实现导出多sheet的excel小实例
- 12.1 Swing与MVC设计模式
- Python中多继承与super()用法
- java反射
- #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif 语句解释
- SELinux policy问题解决思路总结
- 12.2 布局管理器简介
- Linux 用户和用户组