Windows 和 Unix 下动态链接库的区别

Windows 和 Unix 下动态链接库的区别

最近将开发环境转向了freebsd,渐渐的发现很多东西跟Windows下不太一样。我指的是,我以前慢慢想当然理解不太一样。比如,unix下对动态链接库这样的处理,和Windows下的DLL就不太一样。

在此之前,我对 Windows 下面更加了解一些。早几年的 Windows 开发,老是做动态链接,静态链接这些问题纠缠不清;慢慢的特性就比较容易理解了,现在基本上不会出现这个问题类问题困绕了。前一阵子,连通朋友一些写lua的扩展库(windows版本)的时候遇到了几个bug,就是因为链接问题导致的。公司内部的一个项目,在服务器程序上也了连通错误链接 lua 引起的 bug ,在内部 maillist 上争论了好久。,作为 windows 程序员,跟同事中的 unix 程序员分析画面相同的问题时,却老说不到一起去;今天才发现,原来,原来是对动态链接库之间相互理解的不同原因的。

今天,写下一篇文章,或许可以让以后跨平台开发的少朋友走点弯路。

动态链接库在unix下,习惯以.so为文件名结尾(通常还以lib开头)。而Windows下则以.DLL为文件后缀。Windows在处理dll上还有一些容易被人忽略的细节,我曾经为这篇写过一篇博客

如果需要运行时主动加载一个动态链接库,windows下可以使用 LoadLibrary 这个内核 API(在 kernel32.dll 中);unix下是用 dlopen 。Windows下找到 dll 中导出符号的地址,可以用 GetProcAddress ,而 unix 也有对应的api...

这些相互对应的 api ,似乎都具备对等的功能,但实际上是有区别的。

DLL其实和EXE文件一样,同属PE格式的执行引用文件。对于隐含的外部符号,需要把外部符号所在的位置写在PE头部。PE加载器方式相当于PE头部找到依赖的符号表,并加载依赖的其他DLL文件。

但是,unix 上并非如此,所以文件大多为 elf 执行文件格式。当需要它们的外部符号时,可以不写出这些符号所在的位置。相反,通常所以文件本身并不知道它依赖的那些符号在这些符号是由调用 dlopen 的进程运行时提供的。而 unix 下面的执行文件本身会暴露自己静态链接的符号,(可以是自己本身实现的,或者是来自静态库的。文件里链dlopen 将把这些符号通报给 dlopen 加载的 .so 文件最终,完成动态链接。(实际上 dlopen 还可以指定模式,完成更复杂的操作)

因为这个区别,unix下的lua解释器可以完全静态链接所有的lua api;我们作为lua扩展的库,以这样的形式存在被运行时加载不会有任何限制。而Windows下,必须生成一个luacore的DLL 文件,由 lua 解释器于扩展库共享 lua api (还包括 crt 的实现)才不会出问题。

也因为这个区别,VC下才会有让windows开发新手困惑不解的动态链接 CRT,静态链接 CRT,多线程库,单线程库,等等的选项。没点点 windows 开发功力的人,多少都要在上面栽几个头。

从动态链接库的这个设计上来看,我个人感觉,Windows弄的真是糟糕透顶。尤其是对开发者来说是这样的。至少我们在windows下做一个dll文件大家给使用还需要带一个.lib文件;而unix下一般只需要用相应的头文件就够了。对于编写新的.so,缺少的符号最终可以放在那里,直到执行文件来把所有需要的符号联合到一起。可以存在许多dll对另一个dll的隐式依赖关系;而unix下一般不需要让如此如此有隐式依赖关系。这让我们全局替换类似CRT的东西变的困难;而以我自己的编程经验看看,好处却没有多少。

顺便提一下,unix下需要用ldconfig来管理动态库,这比windows下复制DLL到当前目录下就可以用的方式,无疑提高了系统的安全性。

posted @ 2024-03-03 20:19  CharyGao  阅读(26)  评论(0)    收藏  举报