一种平台一个叫法
绝大部分操作系统都支持动态库,它们的工作方式也大抵相同,但称呼却大相径庭。在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.o
gcc elliptical.o -L/libs -lhfcal -o elliptical
尽管你使用的命令和静态存档一模一样,但两者编译的方式不同。因为库是动态的,所以编译器不会在可执行文件中包含库代码,而是插入一段用来查找库的“占位符”代码,并在运行时链接库。
下面来看看程序能否运行。
MinGW和Cygwin的库名
在MinGW和Cygwin上,库名的格式有很多种,hfcal的库名可以是:
libhfcal.dll.a
libhfcal.dll
hfcal.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
中,并export1它,elliptical
就能找到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。问:静态链接和动态链接哪个好?
答:不可一概而论。使用静态链接,可以得到一个小而快的可执行文件,并可以很方便地把它从一台机器拷贝到另一台。而动态链接允许在运行时配置程序。
问:如果不同的程序使用相同的动态库,动态库会加载一次还是多次?这些程序会共享它吗?
答:这取决于操作系统。有的操作系统会为每个进程加载一个动态库,有的则会共享动态库以节省存储器。
问:动态库是配置程序的最好方式吗?
答:通常情况下,配置文件可能比动态库更简单。但如果想连接一些外部设备,通常会用动态库作为驱动。