C专家编程 第5章 对链接的思考 5.2 动态链接的优点

    动态链接的优点:
    可执行文件体积小;
    虽然运行速度稍慢一些,但动态链接能更加有效地利用磁盘空间,而且链接-编辑的时间也会缩短(因为链接器的有些工作被推迟到载入时)

    动态链接的目的之一是ABI
    动态链接的主要目的就是把程序与它们使用的特定的函数库版本中分离开来。取而代之的是,我们约定由系统向程序提供一个接口,该接口保持稳定,不随时间和操作系统的后续版本发生变化。

     接口是介于应用程序和函数库二进制可执行文件所提供的服务之间的接口,所以称它为应用二进制接口(Application Binary Interface, ABI)

    将基于AT&T的SVr4的UNIX世界进行统一的目的就是提供一个单独的ABI。ABI保证函数库存在于所有遵循约定的机器中,并保证接口的完整性。动态链接必须保证4个特定的函数库:libc(C运行时函数库)、libsys(其他系统函数) 、libX(X windowing)和libnsl(网络服务)。其他的函数可以通过静态链接,但最好采用动态链接。

    动态链接单个可执行文件的启动速度稍受影响,但动态链接可以从以下方面提高性能。
    1.动态链接可执行文件比功能相同的静态链接可执行文件的体积小。它能够节省磁盘空间和虚拟内存,因为函数库只有在需要时才被映射到进程中。

    2.所有动态链接到某个特定函数库的可执行文件在运行时共享该函数库的一个单独副本。操作系统内核保证映射到内存中的函数可以被所有使用它们的进程共享。

    动态链接使得函数库的版本升级更为容易。新的函数库可以随时发布。

    最后(虽然并不常见,但仍可能出现)动态链接允许用户在运行时选择需要执行的函数库。这就使为了提高速度或者提高内存使用效率或包含额外的调试信息而创建新版本的函数库是完全有可能的。

    动态链接是一种“just-in-time(JIT)”链接,这意味着程序在运行时必须能够找到他们需要的函数库。链接器通过把库文件名和路径名植入可执行文件中来做到这一点。这意味着,函数库的路径不能随意移动。除非在链接器中进行特别说明。否则,当程序员调用该函数库的函数时,就会在运行时导致失败,并给出这样一条错误信息。
    ld.so.1: main: fatal: libthread.so: can't open file: errno = 2

    任何人都可以创建静态或动态的函数库。只需要简单地编译一些不包含main函数的代码,并把编译所生的.o文件用正确的实用工具进行处理:如果是静态库,则使用ar;如果是动态库,则使用ld。

    系统调用mmap()把文件映射到进程的地址空间中。

    所有的应用程序只使用动态链接。使用静态链接的最大危险在于未来版本的操作系统可能与可执行文件所绑定的系统函数库并不兼容。而且有些函数库(如libaio.so, libsys.so, libsolv.so以及librpcsvs.so等)只能以动态链接的形式使用。 

    静态库被称作archive, 它们通过ar(用于archive的使用工具)来创建和更新。静态库约定在他们的文件名中使用“.a”作为扩展名。

    在SVr3中,还存在一种中间性质的链接,介于静态链接和动态链接之间,称为“静态共享库”(static shared libraries)。在生命期内,它们的地址始终固定,这样它们就可以直接绑定到应用程序中,较之动态链接少了一层中间环节。另外,它们显得不是很灵活,而且需要操作系统提供很多支持。

    动态链接由链接编辑器ld创建。根据约定,动态库的文件扩展名为“.so”,表示“shared object”(共享对象)--- 每一个链接到该函数库的程序共享它的同一份副本。 

    动态链接库的最简单形式可以通过在cc命令上加上-G选项来创建,如下所示:
    %cat tomato.c 
    my_lib_function() {
        printf("library routine called\n");
    } 
    -G来创建动态链接 
    %cc -o libfruit.so -G tomato.c
    然后可以利用这个动态链接库来编写程序,并且使用下面这种方法与函数库进行链接。
    %cat test.c
    int main() {
        my_lib_function();
    } 
    //-L/home/linden 链接时是从那个目录寻找需要链接的函数库 
    //-R/home/linden 运行时是从那个目录寻找需要链接的函数库 
    %cc test.c -L/home/linden -R/home/linden -lfruit
    %a.out

    /*
    ** tomato.h.
    */
    #ifndef TOMATO_H
    #define TOMATO_H 

    void my_lib_function( void ); 

    #endif

    /*
    ** tomato.c
    */
    #include <stdio.h>
    #include "tomato.h"

    void my_lib_function( void ) {
        printf("library routine called\n");
    } 

    /*
    ** tomato_test.c
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include "tomato.h"

    int main( void ){
        my_lib_function();
    
        return EXIT_SUCCESS;
    }
输出:

参考链接:https://blog.csdn.net/TIME_LEAF/article/details/115333179 
    /* 编译生成目标文件。这里是tomato.o */ 
    gcc -c tomato.c
    /* 使用ar命令生成静态函数库。这里是libtomato.a */ 
    ar -cr libtomato.a tomato.o
    /* 完成静态库的制作后,只需库文件libtomato.a、头文件tomato.h与调用程序tomato_test.cpp参与编译,不再需要源文件tomato.cpp。*/
    /* 使用静态函数库生成可执行文件。这里是test.exe。*/ 
    gcc tomato_test.c libtomato.a -o test.exe
    /* 注意:在命令gcc tomato_test.c -ltomato -L./ -o test.exe中,-l代表使用的库,
    ** 由于编译器会按照lib<库名>.a的规则进行搜索,因此这里只需要给出tomato即可,
    ** -L用来指定附加库文件的搜索路径,此处./代表当前目录。*/
    gcc tomato_test.c -ltomato -L./ -o test.exe
    /* 运行test.exe可执行文件 */ 
    test.exe
    /* 生成动态函数库。这里是libtomato.so */ 
    gcc -shared -fpic tomato.c -o libtomato.so
    /* 使用动态函数库,生成可执行文件。这里是test.exe */ 
    gcc tomato_test.c libtomato.so -o test.exe
    /* 注意:由于动态库在程序执行时才被加载,所以如果将动态库libtomato.so删除的话,
    ** 那test.exe就无法执行了。*/

    编译器选项-K pic来为函数库产生与位置无关的代码。与位置无关的代码表示用这种方法产生的代码保证对于任何全局数据的访问都是通过额外的间接方法来完成的。这使他很容易对数据进行重新定位,只要简单地修改全局偏移量表中的其中一个值就可以了。

    类似地,每个函数调用的产生就像是通过过程链接表的某个间接地址所产生的一样。这样,文本
可以很容易地重新定位到任何地方,只要修改一下偏移量表就可以了。所以当代码在运行时被映射
进来时,运行时链接器可以直接把他们放在任何空闲的地方,而代码本身并不需要修改。

    在缺省情况下,编译器并不产生与位置无关的代码,因为额外的指针解引用操作将使程序在运行时稍稍变慢。然而,如果不使用与位置无关的代码,则所产生的代码就会被对应到固定的地址,这对于可执行文件来说确实很好,但对于共享库,速度却要慢一点,因为现在每个全局引用就不得不在运行时通过修改页面安排到固定的位置, 这就使得页面无法共享。

    运行时链接器总能够安排对页面的引用。但是,使用位置无关代码可以极大地简化任务。当然需要权衡一下,位置无关代码与由运行时链接器安排代码相比,速度是快了还是慢了。 根据经验,对于函数库应该始终使用与位置无关的代码。对于共享库,与位置无关的代码显得格外有用,因为每个使用共享库的进程一般都会把它映射到不同的虚拟地址(尽管共享同一份物理副本)

    “纯代码(pure code)”,纯可执行文件是只包含代码(无静态或初始化过的数据)的文件。之所以称为“纯”,是因为它不必进行修改就能被其他特定的进程执行。它从堆栈或者其他(非纯)段引用数据。纯代码可以被共享。如果生成与位置无关的代码(意味着共享),你通常也希望它是纯代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值