<i id='9b5j4'></i>

    1. <acronym id='9b5j4'><em id='9b5j4'></em><td id='9b5j4'><div id='9b5j4'></div></td></acronym><address id='9b5j4'><big id='9b5j4'><big id='9b5j4'></big><legend id='9b5j4'></legend></big></address>
      <i id='9b5j4'><div id='9b5j4'><ins id='9b5j4'></ins></div></i>

        <ins id='9b5j4'></ins>
        <span id='9b5j4'></span>

          <code id='9b5j4'><strong id='9b5j4'></strong></code>
          <fieldset id='9b5j4'></fieldset>

          <dl id='9b5j4'></dl>
        1. <tr id='9b5j4'><strong id='9b5j4'></strong><small id='9b5j4'></small><button id='9b5j4'></button><li id='9b5j4'><noscript id='9b5j4'><big id='9b5j4'></big><dt id='9b5j4'></dt></noscript></li></tr><ol id='9b5j4'><table id='9b5j4'><blockquote id='9b5j4'><tbody id='9b5j4'></tbody></blockquote></table></ol><u id='9b5j4'></u><kbd id='9b5j4'><kbd id='9b5j4'></kbd></kbd>
          1. Linux下如何用GCC编译动态库

            • 时间:
            • 浏览:8
            • 来源:124软件资讯网

                本文主要解决以下几个问题

                1 为什么要使用库?

                2 库的分类

                3 建立自己的库

                或许各人对自己初学 Linux时的情形仍影象尤新吧  。若是没有一个能较好的解决依赖关系的保证理器  ,在Linux下安装软件将是一件及其痛苦的事情 。你装a包时 ,可能会提醒你要先装b包  ,当你费经心力找到b包时  ,可能又会提醒你要先安装c包 。我就曾被这样的事搞的焦头烂额 ,至今一提起rpm仍心有余悸 ,头皮发麻 。说是一朝被蛇咬  ,十年怕井绳怕也不为过  。

                Linux下之以是有这许多的依赖关系 ,其中一个开发原则真是功不行没  。这个原则就是:只管不重复做别人已经做过的事 。换句话说就是只管充实使用别人的劳动结果  。

                这就涉及到怎样有用的举行代码复用 。

                1 为什么要使用库?

                关于代码复用的途径 ,一样平常有两种  。

                粘贴复制

                这是最没有手艺含量的一种方案 。若是代码小 ,则事情量还可以忍受  ,若是代码很重大  ,则此法不行取  。即便有人原意这样做  ,但谁又能保证所有的代码都可获得呢?

                而库的泛起很好的解决了这个问题  。

                库  ,是一种封装机制 ,简朴说把所有的源代码编译成目的代码后打成的包 。

                那么用户怎么能知道这个库提供什么样的接口呢?岂非要用nm等工具逐个扫描?

                不用担忧  ,库的开发者早以把一切都做好了  。除了包罗目的代码的库外 ,www.Linuxidc.com一样平常还会提供一系列的头文件  ,头文件中就包罗了库的接口  。为了让利便用户  ,再加上一个使用说明就差不多完善了  。

                2 库的分类

                2.1 库的分类

                凭据链接时期的差别  ,库又有静态库和动态库之分  。

                静态库是在链接阶段被链接的(似乎是空话  ,但事实就是这样) ,以是天生的可执行文件就不受库的影响了 ,纵然库被删除了  ,法式依然可以乐成运行  。

                有别于静态库  ,动态库的链接是在法式执行的时间被链接的  。以是 ,纵然法式编译完  ,库仍须保留在系统上  ,以供法式运行时挪用  。(TODO:链接动态库时链接阶段到底做了什么)

                2.2 静态库和动态库的比力

                链接静态库实在从某种意义上来说也是一种粘贴复制  ,只不外它操作的工具是目的代码而不是源码而已  。由于静态库被链接后库就直接嵌入可执行文件中了  ,这样就带来了两个问题 。

                首先就是系统空间被铺张了  。这是显而易见的 ,想象一下 ,若是多个法式链接了统一个库  ,则每一个天生的可执行文件就都市有一个库的副本  ,一定会铺张系统空间  。

                再者  ,人非圣贤  ,纵然是经心调试的库  ,也难免会有错  。一旦发现了库中有bug  ,拯救起来就比力贫苦了  。必须逐一把链接该库的法式找出来 ,然后重新编译 。

                而动态库的泛起正填补了静态库的以上毛病  。由于动态库是在法式运行时被链接的  ,以是磁盘上只须保留一份副本  ,因此节约了磁盘空间  。若是发现了bug或要升级也很简朴  ,只要用新的库把原来的替换掉就行了 。

                那么  ,是不是静态库就一无是处了呢?

                答曰:非也非也  。不是有句话么:存在即是合理 。静态库既然没有湮没在滔滔的历史长河中  ,就一定有它的用武之地  。想象一下这样的情形:若是你用libpcap库编了一个法式  ,要给被人运行  ,而他的系统上没有装pcap库  ,该怎么解决呢?最简朴的措施就是编译该法式时把所有要链接的库都链接它们的静态库  ,这样  ,就可以在别人的系统上直接运行该法式了  。

                所谓有得必有失 ,正由于动态库在法式运行时被链接  ,故法式的运行速率和链接静态库的版底细比一定会打折扣  。然而瑕不掩瑜 ,动态库的不足相对于它带来的利益在现今硬件下简直是微不足道的  ,以是链接法式在链接时一样平常是优先链接动态库的 ,除非用-static参数指定链接静态库  。

                2.3 怎样判断一个法式有没有链接动态库?

                谜底是用file适用法式  。

                file法式是用来判断文件类型的  ,在file下令下  ,所有文件都市真相毕露的 。

                顺便说一个技巧  。有时在 windows下用浏览器下载tar.gz或tar.bz2文件 ,后缀名会酿成希奇的tar.tar  ,到Linux有些新手就不知怎么解压了  。但 Linux下的文件类型并不受文件后缀名的影响  ,以是我们可以先用下令file xxx.tar.tar看一下文件类型 ,然后用tar加适当的参数解压  。

                另外  ,还可以借助法式ldd适用法式来判断 。

                ldd是用来打印目的法式(由下令行参数指定)所链接的所有动态库的信息的  ,若是目的法式没有链接动态库  ,则打印“not a dynamic executable”  ,ldd的用法请参考manpage  。

                3 建立自己的库

                3.1 建立动态库

                建立文件hello.c  ,内容如下:

                #include

                void hello(void)

                {

                printf("Hello World\n");

                }

                用下令gcc -shared hello.c -o libhello.so编译为动态库  。可以看到  ,当前目录下多了一个文件libhello.so  。

                [leo@leo test]$ file libhello.so

                libhello.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped

                看到了吧  ,文件类型是shared object了 。

                再编辑一个测试文件test.c  ,内容如下:

                int

                main()

                {

                hello();

                return 0;

                }

                这下可以编译了:)

                [leo@leo test]$ gcc test.c

                /tmp/ccm7w6Mn.o: In function `main':

                test.c:(.text+0x1d): undefined reference to `hello'

                collect2: ld returned 1 exit status

                链接时gcc找不到hello函数  ,编译失败:(  。缘故原由是hello在我们自己建立的库中 ,若是gcc能找到那才教见鬼呢!ok  ,再接再厉  。

                [leo@leo test]$ gcc test.c -lhello

                /usr/lib/gcc/i686-pc-Linux-gnu/4.0.0/../../../../i686-pc-Linux-gnu/bin/ld: cannot find -lhello

                collect2: ld returned 1 exit status

                [leo@leo test]$ gcc test.c -lhello -L.

                [leo@leo test]$

                第一次编译直接编译  ,gcc默认会链接尺度c库  ,但符号名hello剖析不出来  ,故毗连阶段通不外了  。

                现在用gcc test.c -lhello -L.已经编译乐成了  ,默认输出为a.out 。现在来试着运行一下:

                [leo@leo test]$ ./a.out

                ./a.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

                咦  ,怎么回事?原来虽然链接时链接器(dynamic linker)找到了动态库libhello.so  ,但动态加载器(dynamic loader , 一样平常是/lib/ld-Linux.so.2)却没找到  。再来看看ldd的输出:

                [leo@leo test]$ ldd a.out

                Linux-gate.so.1 => (0xffffe000)

                libhello.so => not found

                libc.so.6 => /lib/libc.so.6 (0x40034000)

                /lib/ld-Linux.so.2 (0x40000000)

                果真云云  ,看到没有  ,libhello.so => not found 。

                Linux为我们提供了两种解决要领:

                1.可以把当前路径加入 /etc/ld.so.conf中然后运行ldconfig  ,或者以当前路径为参数运行ldconfig(要有root权限才行) 。

                2.把当前路径加入情况变量LD_LIBRARY_PATH中

                固然  ,若是你以为不会引起杂乱的话 ,可以直接把该库拷入/lib,/usr/lib/等位置(无可制止  ,这样做也要有权限) ,这样链接器和加载器就都可以准确的找到该库了 。

                我们接纳第二种要领:

                [leo@leo test]$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

                [leo@leo test]$ ldd a.out

                Linux-gate.so.1 => (0xffffe000)

                libhello.so => ./libhello.so (0x4001f000)

                libc.so.6 => /lib/libc.so.6 (0x40036000)

                /lib/ld-Linux.so.2 (0x40000000)

                哈哈  ,这下ld-Linux.so.2就可以找到libhello.so这个库了 。

                现在可以直接运行了:

                [leo@leo test]$ ./a.out

                Hello World

                3.2 建立静态库

                仍使用适才的hello.c和test.c 。

                第一步  ,天生目的文件 。

                [leo@leo test]$ gcc -c hello.c

                [leo@leo test]$ ls hello.o -l

                -rw-r--r-- 1 leo users 840 5月 6 12:48 hello.o

                第二步,把目的文件归档  。

                [leo@leo test]$ ar r libhello.a hello.o

                ar: creating libhello.a

                OK,libhello.a就是我们所建立的静态库了  ,简朴吧:)

                [leo@leo test]$ file libhello.a

                libhello.a: current ar archive

                下面一行下令就是教你怎样在法式中链接静态库的:

                [leo@leo test]$ gcc test.c -lhello -L. -static -o hello.static

                我们来用file下令比力一下用动态库和静态库链接的法式的区别:

                [leo@leo test]$ gcc test.c -lhello -L. -o hello.dynamic

                正如前面所说  ,链接器默认会链接动态库(这里是libhello.so)  ,以是只要把上个下令中的 -static参数去掉就可以了  。

                用file适用法式验证一下是否按我们的要求天生了可执行文件:

                [leo@leo test]$ file hello.static hello.dynamic

                hello.static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, statically linked, not stripped

                hello.dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.6, dynamically linked (uses shared libs), not stripped

                不妨顺便训练一下ldd的用法:

                [leo@leo test]$ ldd hello.static hello.dynamic

                hello.static:

                not a dynamic executable

                hello.dynamic:

                Linux-gate.so.1 => (0xffffe000)

                libhello.so => ./libhello.so (0x4001f000)

                libc.so.6 => /lib/libc.so.6 (0x40034000)

                /lib/ld-Linux.so.2 (0x40000000)

                OK ,看来没有问题 ,那就比力一下巨细先:

                [leo@leo test]$ ls -l hello.[ds]*

                -rwxr-xr-x 1 leo users 5911 5月 6 12:54 hello.dynamic

                -rwxr-xr-x 1 leo users 628182 5月 6 12:54 hello.static

                看到区别了吧  ,链接静态库的目的法式和链接动态库的法式比起来简直就是一个庞然大物!

                这么小的法式  ,很难看出执行时间的差异  ,不外为了完整起见  ,照旧看一下time的输出吧:

                [leo@leo test]$ time ./hello.static

                Hello World

                real 0m0.001s

                user 0m0.000s

                sys 0m0.001s

                [leo@leo test]$ time ./hello.dynamic

                Hello World

                real 0m0.001s

                user 0m0.000s

                sys 0m0.001s

                若是法式比力大的话 ,应该效果会很显着的  。