一种平台一个叫法
绝大部分操作系统都支持动态库,它们的工作方式也大抵相同,但称呼却大相径庭。在 Windows 中,动态库通常叫动态链接库,后缀名是.dll;在 Linux 和 Unix 上,它们叫共享目标文件,后缀名.so;而在 Mac 上,它们就叫动态库,后缀名.dylib。尽管后缀名不同,但创建它们的方法相同:
-shared 选项告诉 gcc 你想把.o 目标文件转化为动态库。编译器创建动态库时会把库的名字保存在文件中,假设你在 Linux 中创建了一个叫 libhfcal.so 的库,那么 libhfcal.so 文件就会记住它的库名叫 hfcal。也就是说,一旦你用某个名字编译了库,就不能再修改文件名了,这一点很重要。
若想重命名库,就必须用新的名字重新编译一次。在一些古老的 Mac 系统上,没有-shared 选项。别担心,在这些机器上可以用-dynamiclib 代替。
编译 elliptical 程序
一旦创建了动态库,你就可以像静态库那样使用它。可以像这样建立 elliptical 程序:gcc -I/include -c elliptical.c -o elliptical.ogcc elliptical.o -L/libs -lhfcal -o elliptical
尽管你使用的命令和静态存档一模一样,但两者编译的方式不同。因为库是动态的,所以编译器不会在可执行文件中包含库代码,而是插入一段用来查找库的“占位符”代码,并在运行时链接库。
下面来看看程序能否运行。MinGW 和 Cygwin 的库名在 MinGW 和 Cygwin 上,库名的格式有很多种,hfcal 的库名可以是:libhfcal.dll.alibhfcal.dllhfcal.dll
试驾
你已经在/libs 目录下创建了动态库,并建立了 elliptical 测试程序,现在就来运行一下。hfcal 不在标准目录中,要确保计算机在运行程序时能找到它。
Mac
你可以直接运行程序。当你在 Mac 中编译程序时,文件的完整路径/libs/libhfcal.dylib 保存在可执行文件中,程序启动时知道去哪里找它。
Linux
但 Linux 就不一样了。
在 Linux 和大部分 Unix 中,编译器只会记录 libhfcal.so 库的文件名,而不会包含路径名。也就是说如果不把 hfcal 库保存到标准目录(如/usr/lib),程序就找不到它。为了解决这个问题,Linux 会检查保存在 LD_LIBRARY_PATH 变量中的附加目录。只要把库目录添加到 LD_LIBRARY_PATH 中,并 export 它,1elliptical 就能找到 libhfcal.so。
1 export 是一条 Linux 命令,用来将自定义变量设为环境变量。——译者注
Windows
那用 Cygwin 和 MinGW 版 gcc 编译的代码呢?两种编译器都会创建 Windows 下的 DLL 库与可执行文件。同 Linux 一样,Windows 可执行文件也只保存 hfcal 库的名字,不保存目录名。
不过 Windows 没有用 LD_LIBRARY_PATH 变量去找 hfcal 库。Windows 程序会先在当前目录下查找,如果没找到就去查找保存在 PATH 变量中的目录。
Cygwin
如果用 Cygwin 编译了程序,可以在 Bash shell 中这样运行它:
MinGW
如果用 MinGW 编译了程序,可以在命令提示符中这样运行它:
是不是有点复杂?是的,这就是为什么绝大部分使用动态库的程序要把动态库保存在标准目录下。在 Linux 和 Mac 中,动态库通常保存在/usr/lib 或/usr/local/lib 中;而在 Windows 中,程序员通常把.DLL 和可执行文件保存在同一个目录下。
练习
Head First 健身房的人正打算把跑步机运输到英格兰。跑步机的嵌入式服务器上装的是 Linux,而且已经预装了美版程序。技术人员在/usr/local/lib 下安装了库。技术人员喜欢把库安装在这些目录下,因为它们更“标准”。机器是根据美国人的使用习惯配置的,因此有一些地方需要修改。为了能在英格兰使用,系统需要进行一些修改:把英里(mile)和磅(pound)换成千米(km)和千克(kg)。安装在机器上的软件需要使用这段新代码。因为软件会以动态库的形式链接这段代码,所以只要把代码编译到/usr/local/lib 目录下就行了。假设你已经进入了 hfcal_UK.c 文件的目录,有所有目录的写权限。为了编译新版动态库,你需要输入什么命令?假设跑步机的主程序叫/opt/apps/treadmill,为了运行程序,需要输入什么命令?
练习解答
Head First 健身房的人正打算把跑步机运输到英格兰。跑步机的嵌入式服务器上装的是 Linux,而且已经预装了美版程序。技术人员在/usr/local/lib 下安装了库。技术人员喜欢把库安装在这些目录下,因为它们更“标准”。机器是根据美国人的使用习惯配置的,因此有一些地方需要修改。为了能在英格兰使用,系统需要进行一些修改:把英里(mile)和磅(pound)换成千米(km)和千克(kg)。安装在机器上的软件需要使用这段新代码。因为软件会以动态库的形式链接这段代码,所以只要把代码编译到/usr/local/lib 目录下就行了。假设你已经进入了 hfcal_UK.c 文件的目录,有所有目录的写权限。为了编译新版动态库,你需要输入什么命令?假设跑步机的主程序叫/opt/apps/treadmill,为了运行程序,需要输入什么命令?
试驾
你已经修改了英版跑步机上的代码,我们对比一下美版跑步机。下面这台美版跑步机使用了原版的 libhfcal.so 库。
机器启动时运行了 treadmill 程序,当用户在跑步机上跑了一段时间以后,显示如下:
美版跑步机上的 treadmill 程序动态链接到了 libhfcal.so 库,而 libhfcal.so 是用美版 hfcal 程序编译的。
英版的呢?
英版跑步机安装了相同的 treadmill 程序,但你用 hfcal_UK.c 文件中的源代码重新编译了 libhfcal.so 库。
当用户跑了一段相同的距离以后,显示如下:
正确运行了。
treadmill 程序不需要重新编译就能从新的库中动态获取代码。
有了动态库,就能在运行时替换代码。不用重新编译程序,你就能修改它。如果你有很多程序,它们共享一段相同的代码,通过建立动态库,就可以同时更新所有程序。既然你已经学会了创建动态库,也就成为了一名更厉害的 C 程序员。
今日主题:两位著名软件模块化粉丝正在讨论静态链接与动态链接的利弊。
静态 :动态 :我们都赞成写模块化程序。当然。顺理成章的一件事。不错。让代码易于管理。对。这样就能写大程序了。大程序?嗯,把所有要用到的东西都放进一个可执行文件。这可不是什么好主意。何出此言?老朋友。我认为程序应该由多个小文件链接而成,只有在运行时才链接它们。啊?(大笑)……你不是在开玩笑吧?我是认真的。啊?多个独立文件?仓促地链接在一起?!不是仓促,是动态。这是混乱的源头!这样做我就能在之后改变主意。你应该一次性就把事情做对。不可能一次性就做对,所有大程序都应该动态链接。所有程序?是的。那 Linux 内核怎么算?够大了吧?它可是…………静态链接的,我知道。你赢了一次。静态链接的机动性不强,但用起来很简单,一个文件打天下,如果你想安装程序,只要拷贝可执行文件即可,不需要 DLL 之类的鬼东西。我们谁也说服不了谁。看样子我不能改变你的想法。是的。所以你也是静态链接的。
要点
动态库在运行时链接程序。
用一个或多个目标文件创建动态库。
在一些机器上,需要用
-fPIC 选项来编译目标文件。
-fPIC 令目标代码位置无关。
在一些机器上,可以省略-fPIC 。
-shared 编译选项可以创建动态库。
动态库在不同机器上名字不同。
如果把动态库保存在标准目录中,生活会变得更简单。
不然,就需要设置 PATH 变量和 LD_LIBRARY_PATH 变量。
这里没有蠢问题问:为什么动态库在不同操作系统中如此不同?答:操作系统喜欢优化加载动态库的方式,因此不同操作系统对动态库制定了不同的需求。问:我想改变动态库的名字,于是重命名了文件名,但编译器找不到它,为什么?答:编译器在编译动态库时会在文件中保存库名。如果你重命名了文件,文件中的名字还是没变。如果想修改动态库的名字就必须重新编译它。问:为什么 Cygwin 的动态库文件支持多种不同的命名方式?答:因为 Cygwin 专门用来在 Windows 上编译 Unix 软件。Cygwin 会创建一个类似 Unix 的环境,因此借鉴了很多 Unix 的命名约定。比如用.a 来命名库,即使它们是动态 DLL。问:Cygwin 动态库是真正的 DLL 吗?答:是的,但因为它们是基于 Cygwin 的,如果你想在一般的 Windows 程序中使用 Cygwin 动态库,还需要做一些工作。问:为什么 MinGW 动态库的命名格式和 Cygwin 一样?答:这两个项目的关系十分紧密,而且共享了很多代码。它们最大的区别是用 MinGW 编译的程序在没有安装 Cygwin 的计算机上也能够运行。问:为什么 Linux 不直接在可执行文件中保存库路径名?那样不就可以不用设置 LD_LIBRARY_PATH 了吗?答:这是一种设计上的选择,如果不保存路径名,程序就可以使用不同版本的库。当你要开发新的库时,这种设计的好处就特别明显。问:为什么 Cygwin 不用 LD_LIBRARY_PATH 来查找库?答:因为 Cygwin 使用 Windows 的 DLL,Windows 会用 PATH 变量来加载 DLL。问:静态链接和动态链接哪个好?答:不可一概而论。使用静态链接,可以得到一个小而快的可执行文件,并可以很方便地把它从一台机器拷贝到另一台。而动态链接允许在运行时配置程序。问:如果不同的程序使用相同的动态库,动态库会加载一次还是多次?这些程序会共享它吗?答:这取决于操作系统。有的操作系统会为每个进程加载一个动态库,有的则会共享动态库以节省存储器。问:动态库是配置程序的最好方式吗?答:通常情况下,配置文件可能比动态库更简单。但如果想连接一些外部设备,通常会用动态库作为驱动。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论