<p><strong>1.库文件简介</strong> 

 </p> 

<p>库文件是一个包含了编译后代码、数据的文件,用于与程序其他代码连编,它可以使得程序模块化、编译速度更快,并且易于更新。库文件分为三种(<strong>实质为两种,在随后两句话有解释</strong> 

):静态库(在程序之前就已经装载进其中了)、共享库(在程序启动之时加载进去,在程序直接共享)、动态加载库(dynamically loaded,DL)(在程序运行中任何时候都可以被加载进程序中使用,<strong>事实上DL并非是一个完全不同的库类型,共享库可以用作DL而被动态加载(静态库在Linux貌似无法用dlopen加载)</strong> 

。注意有些人使用dynamically <em>linked</em> 

 libraries (DLLs)来指代共享库,有些人使用DLL这个词来形容任何可以被用作DL的库文件,这个请区分对待。</p> 

<p>在具体使用中,我们应该多使用共享库,这使得用户可以独立于使用该库文件的程序而更新库。DL的确非常有用,但有时候我们可能并不需要那些灵活性,而对于静态库,由于更新起来实在费劲,我们一般不使用。</p> 

<p><strong>2.静态库的建立</strong> 

</p> 

<p>静 

态库就是一堆普通的目标文件(object 

file),习惯上静态库以.a为后缀,这是使用ar命令生成的。静态库允许用户不用重新编译代码就可以链接程序,以节省重新编译的时间,其实这个时间已 

经在强大的机器配置和快速的编译器中显得微不足道了,这个常常用来提供程序而不是源代码。速度上,静态ELF(Executable and 

Linking Format)库文件比共享库或者动态加载库快1%-5%,但实际上常常因为其他因素而并不一定快。</p> 

<p>我们写主文件prog.c:</p> 

<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #include 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: <span style="color: #0000ff;">void</span> 

 ctest1(<span style="color: #0000ff;">int</span> 

 *); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: <span style="color: #0000ff;">void</span> 

 ctest2(<span style="color: #0000ff;">int</span> 

 *); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">int</span> 

 main() 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: <span style="color: #0000ff;">int</span> 

 x; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: ctest1(&x); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: printf("<span style="color: #8b0000;">Valx=%d/n</span> 

",x); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11: <span style="color: #0000ff;">return</span> 

 0; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: } 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: </pre> 

</pre> 

<p>然后写这两个函数的实现:</p> 

<p>ctest1.c</p> 

<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: <span style="color: #0000ff;">void</span> 

 ctest1(<span style="color: #0000ff;">int</span> 

 *i) 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: *i=5; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: } </pre> 

</pre> 

<p>ctest2.c</p> 

<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: <span style="color: #0000ff;">void</span> 

 ctest2(<span style="color: #0000ff;">int</span> 

 *i) 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: *i=100; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: }</pre> 

</pre> 

<p>我们首先编译这两个函数实现的源文件:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -Wall -c ctest1.c ctest2.c 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls 

 <br> 

ctest1.c ctest1.o ctest2.c ctest2.o prog.c</p> 

</blockquote> 

<p>然后创建静态库libctest.a:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -cvq libctest.a ctest1.o ctest2.o</p> 

<p>a - ctest1.o 

 <br> 

a - ctest2.o</p> 

</blockquote> 

<p>我们查看一下这个库中的文件:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -t libctest.a 

 <br> 

ctest1.o 

 <br> 

ctest2.o</p> 

</blockquote> 

<p>此时我们可以编译我们的程序了,注意-l选项,后边的参数是去掉lib和.a的部分,并且需要放在要编译的文件名之后,否则会报错。:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -o test prog.c -L./ –lctest 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls 

 <br> 

ctest1.c ctest1.o ctest2.c ctest2.o libctest.a prog.c test 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ./test 

 <br> 

Valx=5</p> 

</blockquote> 

<p><strong>3.共享库的建立</strong> 

</p> 

<p>共享库是在程序启动时加载的库文件。当共享库加载完毕后所有启动的起来的程序都将使用新的共享库。在创建共享库之前,还需要了解一些知识:</p> 

<ul> 

<li>命名规则: 


<ul> 

<li>每一个共享库都有一个soname,一般都形如libname.so.versionNumber,其中versionNumber 

每当接口发生改变时都要增加,一个完全的soname的前缀应该是它所在目录,在一个实际系统中,一个完整的soname只是共享库文件的real 

name的符号链接。<strong>程序运行时在内部列出所需的共享库时使用的就是soname。</strong> 

 </li> 

<li>每一个共享库也有一个real name,这是包含实际代码的文件名,real name使用soname为前缀,并且在后边添加一些信息,一般都形如soname.MinorNumber.ReleaseNumber。 最后的releaseNumber可有可无。<strong>这个是生成共享库时实际文件的名称。</strong> 

 </li> 

<li>同时,在编译器要求使用一个共享库时使用的名字称为linker name,一般都是去掉版本号的soname,<strong>用于gcc中-lname这样的选项的编译。</strong> 

 </li> 

<li>这几个名字的关系:你在创建实际库文件中指定libreadline.so.3.0为real name 

,并且使用符号链接创建soname ->libreadline.so.3和linker name-> 

/usr/lib/libreadline.so。 </li> 

</ul> 

</li> 

<li>放置位置: 


<ul> 

<li>GNU标准推荐将所有默认的库安装在/usr/local/lib,这指的是开发者源代码默认的位置。 </li> 

<li>FHS指出大多数的库文件应该放在/usr/lib,而启动所需的库则应该放在/lib中,而非系统库应该放在/usr/local/lib。这指的是发行版默认的位置,这两个标准并没有矛盾。 </li> 

</ul> 

</li> 

</ul> 

<p>共享库的主要有三个步骤:</p> 

<ul> 

<li>创建目标代码。 </li> 

<li>创建库。 </li> 

<li>使用符号链接创建默认版本的共享库(可选)。 </li> 

</ul> 

<p>现在我们举个例子来说明,首先我们编译源代码,使用-fPIC选项生成共享库所需的位置独立代码(position-independent code (PIC)):</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -fPIC -c *.c 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls 

 <br> 

ctest1.c ctest1.o ctest2.c ctest2.o prog.c prog.o</p> 

</blockquote> 

<p>然后我们创建库文件:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0 *.o 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls 

 <br> 

ctest1.c ctest1.o ctest2.c ctest2.o <strong>libctest.so.1.0</strong> 

 prog.c prog.o</p> 

</blockquote> 

<p>-shared选项指明生成共享目标文件,-W1(注意是小写L而不是一)指明传入链接器的参数,在此我们设定了该库的<strong>soname</strong> 

为libctest.so.1,-o则指明了生成的目标库文件为libctest.so.1.0(这个就是<strong>real name</strong> 

)。</p> 

<p>最后创建所需的符号链接:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo mv libctest.so.1.0 /usr/local/lib/libctest.so.1.0 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so.1 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so</p> 

</blockquote> 

<p><strong>创建的libctest.so就是上面所谓linker name,用于编译时-lctest选项。</strong> 

</p> 

<p><strong>创建的libctest.so.1就是soname,我们在上边说过程序在运行时需要这个名字的符号链接。</strong> 

</p> 

<p>此时我们的共享库就建好了,接着我们编译程序:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -L/usr/local/lib prog.c -lctest -o prog 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls 

 <br> 

ctest1.c ctest1.o ctest2.c ctest2.o prog prog.c prog.o</p> 

</blockquote> 

<p>我们编译完毕,该库并不会包含在可执行文件中,只有在执行时来会动态加载进来。我们可以通过ldd列出一个可执行程序所有的依赖,在我的系统中还找不到/usr/local/bin的路径:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog 

 <br> 

 linux-gate.so.1 => (0x00a5c000) 

 <br> 

 libctest.so.1 => not found 

 <br> 

 libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00a6f000) 

 <br> 

 /lib/ld-linux.so.2 (0x00451000)</p> 

</blockquote> 

<p>此时,运行会报找不到库的错误:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog 

 <br> 

./prog: error while loading shared libraries: libctest.so.1: cannot open shared object file: No such file or directory</p> 

</blockquote> 

<p>我们可以将所需库的路径加入到系统路径中,有三种方法可以完成:</p> 

<ul> 

<li>A.在/etc/ld.so.conf中加入所在路径,然后执行ldconfig配置链接器运行时绑定配置。你也可以创建一个文件,将路径写入,然后使用ldconfig –f filename将配置写入。 </li> 

<li>B.修改<tt>LD_LIBRARY_PATH</tt> 

环境变量(Linux下,AIX下为LIBPATH),在其中添加路径。若你直接在.bashrc文件中配置则重启后不失效,否则在shell中设置重启后失效。 </li> 

</ul> 

<p>我们使用A方法中的-f选项:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ vi libctest.conf 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ldconfig -f libctest.conf 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog 

 <br> 

Valx=5 

 <br> 

gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog 

 <br> 

 linux-gate.so.1 => (0x00f6f000) 

 <br> 

 libctest.so.1 => /usr/local/lib/libctest.so.1 (0x005d9000) 

 <br> 

 libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00718000) 

 <br> 

 /lib/ld-linux.so.2 (0x001e6000)</p> 

</blockquote> 

<p>其中libctest.conf中写入路径:/usr/local/lib。程序运行正常。</p> 

<p><strong>4.动态加载库的使用</strong> 

</p> 

<p>动态加载库是在非程序启动时动态加载进入程序的库,这对于实现插件或动态模块有很大的帮助。在Linux中,动态加载库的形式并不特殊,它使用上述两种程序库,使用提供的API在程序运行时动态加载。注意,在不同平台上动态加载库的API并不相同,所以可能会有移植问题出现。</p> 

<p>我们可以通过nm命令先查看一下我们创建的库里面有哪些symbol(可以理解为函数方法)供我们使用:</p> 

<blockquote> 

<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib$ nm /usr/local/lib/libctest.so 

 <br> 

00001f18 a _DYNAMIC 

 <br> 

00001ff4 a _GLOBAL_OFFSET_TABLE_ 

 <br> 

 w _Jv_RegisterClasses 

 <br> 

00001f08 d __CTOR_END__ 

 <br> 

00001f04 d __CTOR_LIST__ 

 <br> 

00001f10 d __DTOR_END__ 

 <br> 

00001f0c d __DTOR_LIST__ 

 <br> 

000005a0 r __FRAME_END__ 

 <br> 

00001f14 d __JCR_END__ 

 <br> 

00001f14 d __JCR_LIST__ 

 <br> 

00002014 A __bss_start 

 <br> 

 w __cxa_finalize@@GLIBC_2.1.3 

 <br> 

00000540 t __do_global_ctors_aux 

 <br> 

00000420 t __do_global_dtors_aux 

 <br> 

00002010 d __dso_handle 

 <br> 

 w __gmon_start__ 

 <br> 

000004d7 t __i686.get_pc_thunk.bx 

 <br> 

00002014 A _edata 

 <br> 

0000201c A _end 

 <br> 

00000578 T _fini 

 <br> 

000003a0 T _init 

 <br> 

00002014 b completed.7021 

 <br> 

000004dc T ctest1 

 <br> 

000004ec T ctest2 

 <br> 

00002018 b dtor_idx.7023 

 <br> 

000004a0 t frame_dummy 

 <br> 

000004fc T main 

 <br> 

 U printf@@GLIBC_2.0</p> 

</blockquote> 

<p>这个命令对静态库和共享库都支持,第二列为symbol类型,小写字母表示符号是本地的,大写字母表示符号是全局(外部)的,几个常见的字母含义如下:T为代码段普通定义,D为已初始化数据段,B为未初始化数据段,U为未定义(用到该符号但是没有在该库中定义)。</p> 

<p>我们创建<tt>ctest.h:</tt> 

</p> 

<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #ifndef CTEST_H 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: #define CTEST_H 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: #ifdef __cplusplus 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">extern</span> 

 "<span style="color: #8b0000;">C</span> 

" { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: #endif 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: <span style="color: #0000ff;">void</span> 

 ctest1(<span style="color: #0000ff;">int</span> 

 *); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: <span style="color: #0000ff;">void</span> 

 ctest2(<span style="color: #0000ff;">int</span> 

 *); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11: #ifdef __cplusplus 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: } 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: #endif 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 14: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 15: #endif</pre> 

</pre> 

<p>这里使用extern C是为了使得该库既可以用于C语言又可以用于C++。</p> 

<p>我们动态加载库进来:progdl.c</p> 

<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #include 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: #include 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: #include "<span style="color: #8b0000;">ctest.h</span> 

" 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">int</span> 

 main(<span style="color: #0000ff;">int</span> 

 argc, <span style="color: #0000ff;">char</span> 

 **argv) 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: <span style="color: #0000ff;">void</span> 

 *lib_handle; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: <span style="color: #0000ff;">double</span> 

 (*fn)(<span style="color: #0000ff;">int</span> 

 *); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: <span style="color: #0000ff;">int</span> 

 x; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10: <span style="color: #0000ff;">char</span> 

 *error; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: lib_handle = dlopen("<span style="color: #8b0000;">/usr/local/lib/libctest.so</span> 

", RTLD_LAZY); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: <span style="color: #0000ff;">if</span> 

 (!lib_handle) 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 14: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 15: fprintf(stderr, "<span style="color: #8b0000;">%s/n</span> 

", dlerror()); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 16: exit(1); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 17: } 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 18: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 19: fn = dlsym(lib_handle, "<span style="color: #8b0000;">ctest1</span> 

"); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 20: <span style="color: #0000ff;">if</span> 

 ((error = dlerror()) != NULL) 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 21: { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 22: fprintf(stderr, "<span style="color: #8b0000;">%s/n</span> 

", error); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 23: exit(1); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 24: } 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 25: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 26: (*fn)(&x); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 27: printf("<span style="color: #8b0000;">Valx=%d/n</span> 

",x); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 28: 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 29: dlclose(lib_handle); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 30: <span style="color: #0000ff;">return</span> 

 0; 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 31: }</pre> 

</pre> 

<p>里面的方法解释如下:</p> 

<ul> 

<li>void * dlopen(const char *filename, int flag); <br> 

若filename为绝对路 

径,那么dlopen就会试图打开它而不搜索相关路径,否则就现在环境变量LD_LIBRARY_PATH处搜索,然后在/etc 

/ld.so.cache以及/lib和/usr/lib搜索。flag我们只解释两个常用的选项:若为RTLD_LAZY则表示在动态库执行时解决未定 

义符号问题,而RTLD_NOW则表示在dlopen返回前解决未定义符号问题。当你调试时你应该用RTLD_NOW,这个时候若存在未解决的引用程序还 

可以继续进行。另外,RTLD_NOW选项可能会使打开库的这个操作稍微慢一点,但是以后寻找函数时就会快一点。注意,若程序库相互依赖则应该按依赖顺序 

依次载入,比如X依赖Y,那么要先载入Y然后再载入X。返回的是一个句柄,若失败则返回null. 


</li> 

<li>char *dlerror(void); 

 <br> 

报告任何上一次对加载库操作的错误。两次调用期间若有操作错误则第二次会报告, 否则第二次则返回null——它报告完错误就等待下一个错误的发生,上一次错误的情况一旦报告就不再提及。 


</li> 

<li>void *dlsym(void *handle, const char *symbol); 

 <br> 

寻找对应symbol的函数方法,handle就是dlopen返回的句柄。一般如下使用: 

 <br><pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: dlerror(); <span style="color: #008000;">/* clear error code */</span> 


</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: s = (actual_type) dlsym(handle, symbol_being_searched_for); 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: <span style="color: #0000ff;">if</span> 

 ((err = dlerror()) != NULL) { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: <span style="color: #008000;">/* handle error, the symbol wasn't found */</span> 


</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: } <span style="color: #0000ff;">else</span> 

 { 

</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: <span style="color: #008000;">/* symbol found, its value is in s */</span> 


</pre> 

<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: }</pre> 

</pre> 

</li> 

<li>int dlclose(void *handle); 

 <br> 

关闭一个动态加载库。当一个动态库被加载多次时,你需要用同样次数dlclose该动态库才可以deallocated. 


</li> 

</ul> 

<p>我们编译该代码gcc -g -rdynamic -o progdl progdl.c -ldl,即可得到可执行文件(其中-g选项是为了gdb调试所用),其中的库为动态加载后又关闭的。我们使用gdb看一下代码:</p> 

<blockquote> 

<p>(gdb) b main 

 <br> 

Breakpoint 1 at 0x804878d: file progdl.c, line 12. 

 <br> 

(gdb) r 

 <br> 

Starting program: /home/gnuhpc/MyCode/lib/dynamic/progdl </p> 

<p>Breakpoint 1, main (argc=1, argv=0xbffff4a4) at progdl.c:12 

 <br> 

12 lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY); 

 <br> 

(gdb) f 

 <br> 

#0 main (argc=1, argv=0xbffff4a4) at progdl.c:12 

 <br> 

12 lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY); 

 <br> 

(gdb) s 

 <br> 

13 if (!lib_handle) 

 <br> 

(gdb) n 

 <br> 

19 fn = dlsym(lib_handle, "ctest1"); 

 <br> 

(gdb) 

 <br> 

20 if ((error = dlerror()) != NULL) <br> 

(gdb) 

 <br> 

26 (*fn)(&x); 

 <br> 

(gdb) 

 <br> 

27 printf("Valx=%d/n",x); 

 <br> 

(gdb) p x 

 <br> 

$1 = 5 

 <br> 

(gdb) p fn 

 <br> 

$2 = (double (*)(int *)) 0x28c4dc </p> 

</blockquote> 

<p>可以看到fn获得了ctest1的地址。</p>