Linux下的动态共享链接库的优点有哪些

博客访问: 10598
博文数量: 15
博客积分: 0
博客等级: 民兵
技术积分: 11
注册时间:
分类: LINUX 15:54:30
原文地址: 作者:
&&& 动态共享库的工作方式与静态链接库不同。对于每个使用静态链接库的应用程序而言,在应用程序中都存在着静态链接库拷贝。但是动态共享库却不是这样的,动态共享库是被所有使用它的应用程序共享的,无论调用一个动态共享库的进程有多少,系统中始终只运行着一个动态共享库,这里动态共享库中“共享”的含义。至于“动态”,则主要强调的是链接发生在什么阶段。对于静态链接库而言,链接过程发生在编译阶段,操作系统在加载程序时不再对程序做任何改变,因此“静态”链接;然而对于使用动态共享库的程序而言,编译器在编译程序时只知道程序
&& 将要使用到某个动态共享库中的某个符号。至于这个符号所对应的具体实体,在编译时则是未知的。从符号转换为实体的工作留给了操作系统在加载程序时支完成。这样一个转换过程实质上就是把程序的链接过程推迟到了程序执行时来完成。这与在编译期间完成链接雷管显然是不一样的。这也是动态共享库被冠以“动态”的原因。&& 动态共享库有以下的优点,使它在Linux开发中比静态链接库更加的流行。(1) 节省内存&& 动态共享库无论被多少应用程序使用,在内存中都只存在一个动态共享库的副本,而不像静态链接库那样,一个应用程序在运行中用到静态链
接库,就会有多个静态链接库的副本 。(2) 节省磁盘&&& 这和节省内存有点相似,同样这也是由于静态链接库存在多个静态链接库的副本造成的。同样的应用程序,使用动态共享库编译出的版本通常比使用静态链接库编译出来的版本要小。因此,在嵌入式系统开发中使用动态共享库也不节省空间,提供了一种很好的选择。(3) 便于软件修复与升级&&& 由于动态共享是独立于应用程序存在的,因此,用新版本的动态共享库替旧版本的工作将变得非常容易。如果使用静态链接库的话,假设在一个静态库中发现了一个bug,那么要修正这个bug的话,就要重新编译所有使用这个静态库的应用程序,使用这个静态库的应用程序有很多的话,可以想像工作量是有多大。(4) 提高性能&&& 与采用静态链接库臃肿的应用程序相比,采用动态共享库的应用程序明显“苗条”得多,这样当操作系统加载应用程序时,是需要把应用程序
复制到内存中的,这样的“苗条”的动态链接库也就有了很大的优势,同时提高了程序的性能。
&& 当然,动态链接库在有上述这些优势的同时,也有以下的几个劣势。复杂性,兼容性,调试困难。但是它在Linux上使用频率上仍然比静态链接库要高的多。应用的更加广泛。
动态共享库的重要概念-----soname&& 动态共享库有一个重要的概念---soname。动态共享库在Linux系统上是以文件的形式存在的,这样,每个动态共享库也就有一个文件名,假设把动态共享库的文件名定义为filename。那么必须牢记一件事性就是soname和filename不是一个概念的。在Linux下,每个动态共享库都必须被赋予一个按特定的名称,Linux的文档将其称之为soname。soname通常包含共享库的名字和版本号,通过soname,系统或链接器可以唯一地确定一个动态共享库。比如Linux的C函数库的soname是libc.so.6,这里,c是函数库的名字,6是该动态共享库的版本号。& 然而,应用程序并不是直接和名字为soname的文件相链接的。比如,对于Linux C库而言,尽管通常在系统中能够找到名为libc.so.6的文件,但这个文件实际上却只是一个软连接也就是符号链接。这里清楚地显示出libc.so.6是一个到libc-2.3.2.so的软连接。那么,这个软连接是由ldconfig建立的。ldconfig做的事情就是配置动态链接器的运行时绑定。对于一个编译好的动态链接库,如何知道它的soname是什么呢?可以使用readelf。readelf有一个选项-d,这个选项可以打印出ELF文件中dynameic段的内容。在ELF中的dynamic段中就包含着动态共享库的soname。在Linux系统中存在着一个文件 /etc/ld.so.conf。这个文件中的每一行指定了ldconfig应当搜索的目录,在这些目录下面存放着系统运行中所需要的动态共享库文件,以.so.x.x结尾的文件;ldconfig依次搜索这些目录,为找到的每个动态共享库文件建立一个到共soname的软链接。
动态共享库的构建和安装&& 在为动态共享库编译目标文件时,GCC编译器的下面这些选项是需要的:
-share:这个选项告诉GCC生成共享目标文件。生成的共享目标文件可以与其他目标文件链接在一起构成一个可自执行文件,此选项必须和-fPIC等选项联合使用。
-fPIC:这个选项告诉GCC生成位置无关代码(Position-Independent Code,PIC)在共享库中应当使用位置无关代码.
-Wl,-soname,xxx:这是一个特殊的GCC选项。通过该选项,GCC会把-soname,xxx作为一个命令行选项传递给链接器ld,ld拿这个选项来做什么呢?在每个动态共享库的dynamic段都有一个soname项。-soname,xxx选项的作用就是告诉链接器ld,将要链接产生的文件是一个动态共享库,它的soname是xxx.比如,如果编译版本号为6的Linux C库,就应当在生成libc-2.3.2.so时给GCC指定链接选项-Wl,-soname,libc.so.6.
& 构建动态共享库的最后一个步骤通常是下面的一个命令行:
gcc -share -Wl,soname,xxx -o libname filelist liblist&& 在这个命令行中,libname表示最终产生的动态共享库文件名。比如libc-2.3.2.so.filelist是一组目标文件列表,它表明了哪些编译好的目标文件将放到最终产生的动态共享库里面去。由于动态共享库可以依赖于其他共享库,因此这一项指定了最终产生的动态共享库将依赖的那些共享库的列表。&& 动态共享库在构建完成之后,如果需要安装到特定和系统目录下(如最常见的/usr/local/lib目录),只需要将动态共享库文件复制到相应的目录下即可。然后运行ldconfig建立以soname命名的软连接,前提是共享库所在的目录包含在/etc/ld.so.conf文件中,此外 ,为了让链接器能够以标准的方式与动态共享库链接,还需要创建一个不带版本号的软连接。我们可以看到在Linxu系统下的lib目录下有很多的的.so.XX 的文件,这就是系统中使用到的动态共享库
其中我们可以看到libc.so.6.这个是linux下的C函数库文件。我们可以看到这是一个符号链接,真正的文件是libc-2.10.2.so.
#lib$ ls -l libc.so.6lrwxrwxrwx 1 root root 14 2009-12-09 20:59 libc.so.6 -> libc-2.10.1.so#lib$ readelf -d libc-2.10.1.soDynamic section at offset 0x168b40 contains 26 entries:&&Tag Type Name/Value&0x0001 (NEEDED) Shared library: [ld-linux-x86-64.so.2]&0x000e (SONAME) Library soname: [libc.so.6]&0x000c (INIT) 0x1e7b0&0x001a (FINI_ARRAY) 0x365740&0x001c (FINI_ARRAYSZ) 8 (bytes)&0x0004 (HASH) 0x1620d8&0xffffef5 (GNU_HASH) 0x2b8&0x0005 (STRTAB) 0x10560&0x0006 (SYMTAB) 0x3c00&0x000a (STRSZ) 21963 (bytes)&0x000b (SYMENT) 24 (bytes)&0x0003 (PLTGOT) 0x368fe8&0x0002 (PLTRELSZ) 192 (bytes)&0x0014 (PLTREL) RELA&0x0017 (JMPREL) 0x1e5d8&0x0007 (RELA) 0x16e38&0x0008 (RELASZ) 30624 (bytes)&0x0009 (RELAENT) 24 (bytes)&0xffffffc (VERDEF) 0x16bf8&0xffffffd (VERDEFNUM) 15&0x001e (FLAGS) STATIC_TLS&0xffffffe (VERNEED) 0x16e08&0xfffffff (VERNEEDNUM) 1&0xffffff0 (VERSYM) 0x15b2c&0xffffff9 (RELACOUNT) 1190&0x0000 (NULL) 0x0下面以foo.c和bar.c 文件为例:如何构建动态共享库
#:~/program$ gcc -fPIC -g -c -o foo.o foo.c#~/program$ gcc -fPIC -g -c -o bar.o bar.c#:~/program$ gcc -shared -g -Wl,-soname,libfoobar.so.0 -o libfoobar.so.0.0 foo.o bar.o -lc#:~/program$ sudo cp libfoobar.so.0.0 /usr/local/lib#:~/program$ sudo ldconfig#:~/program$ cd /usr/local/lib#:/usr/local/lib$ lslibfoobar.so.0 libfoobar.so.0.0 python2.6#:/usr/local/lib$ ln -sf libfoobar.so.0 libfoobar.soln: creating symbolic link `libfoobar.so': Permission denied#:/usr/local/lib$ sudo ln -sf libfoobar.so.0 libfoobar.so#:/usr/local/lib$ #:~/program$ gcc -g -o foobar main.c -lfoobar#:~/program$ ./foobarThis is foo!library2 is foo()=fooThis is library1 is calledbar()=bar&&& 这里的共享库的soname指定为libfoobar.so.0,最终生成的动态共享库文件为libfoobar.so.0.0,foo.o和bar.o是要放到libfoobar.so.0-.0中的目标文件列表,-lc则是libfoobar.so.0.0所依赖的共享库列表,这里只有一个Linux C库被依赖。接着把libfoobar.so.0.0安装到/usr/local/lib目录上去,然后运行ldconfig,同时为了让链接器能够链接上libfoobar.so.0.0,还要创建一个指向libfoorbar.so.0和符号连接。&& 这里简要的分析了一下Linux动态共享库的使用,为以后自己的使用做个记录。
阅读(260) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
请登录后评论。没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!Linux 动态库剖析
库用于将相似函数打包在一个单元中。然后这些单元就可为其他开发人员所共享,并因此有了模块化编程这种说法 — 即,从模块中构建程序。Linux 支持两种类型的库,每一种库都有各自的优缺点。静态库包含在编译时静态绑定到一个程序的函数。动态库则不同,它是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。图 1 展示了 Linux 中的库的层次结构。图 1. Linux 中的库层次结构使用共享库的方法有两种:您既可以在运行时动态链接库,也可以动态加载库并在程序控制之下使用它们。本文对这两种方法都做了探讨。静态库较适宜于较小的应用程序,因为它们只需要最小限度的函数。而对于需要多个库的应用程序来说,则适合使用共享库,因为它们可以减少应用程序对内存(包括运行时中的磁盘占用和内存占用)的占用。这是因为多个应用程序可以同时使用一个共享库;因此,每次只需要在内存上复制一个库。要是静态库的话,每一个运行的程序都要有一份库的副本。GNU/Linux 提供两种处理共享库的方法(每种方法都源于 Sun Solaris)。您可以动态地将程序和共享库链接并让 Linux 在执行时加载库(如果它已经在内存中了,则无需再加载)。另外一种方法是使用一个称为动态加载的过程,这样程序可以有选择地调用库中的函数。使用动态加载过程,程序可以先加载一个特定的库(已加载则不必),然后调用该库中的某一特定函数(图 2 展示了这两种方法)。这是构建支持插件的应用程序的一个普遍的方法。我稍候将在本文探讨并示范该应用程序编程接口(API)。图 2. 静态链接与动态链接用 Linux 进行动态链接现在,让我们深入探讨一下使用 Linux 中的动态链接的共享库的过程。当用户启动一个应用程序时,它们正在调用一个可执行和链接格式(Executable and Linking Format,ELF)映像。内核首先将 ELF 映像加载到用户空间虚拟内存中。然后内核会注意到一个称为 .interp 的 ELF 部分,它指明了将要被使用的动态链接器(/lib/ld-linux.so),如清单 1 所示。这与 UNIX® 中的脚本文件的解释器定义(#!/bin/sh)很相似:只是用在了不同的上下文中。清单 1. 使用 readelf 来显示程序标题mtj@camus:~/dl$ readelf -l dl
Elf file type is EXEC (Executable file)
Entry point 0x8048618
There are 7 program headers, starting at offset 52
Program Headers:
FileSiz MemSiz
0xxxx000e0 0x000e0 R E 0x4
[Requesting program interpreter: /lib/ld-linux.so.2]
0xxxx58 R E 0x1000
0xxxx28 RW
0x0d0 0x000d0 RW
0xxxx00 RW
mtj@camus:~dl$注意,ld-linux.so 本身就是一个 ELF 共享库,但它是静态编译的并且不具备共享库依赖项。当需要动态链接时,内核会引导动态链接(ELF 解释器),该链接首先会初始化自身,然后加载指定的共享对象(已加载则不必)。接着它会执行必要的再定位,包括目标共享对象所使用的共享对象。LD_LIBRARY_PATH 环境变量定义查找可用共享对象的位置。定义完成后,控制权会被传回到初始程序以开始执行。再定位是通过一个称为 Global Offset Table(GOT)和 Procedure Linkage Table(PLT)的间接机制来处理的。这些表格提供了 ld-linux.so 在再定位过程中加载的外部函数和数据的地址。这意味着无需改动需要间接机制(即,使用这些表格)的代码:只需要调整这些表格。一旦进行加载,或者只要需要给定的函数,就可以发生再定位(稍候在
小节中会看到更多的差别)。再定位完成后,动态链接器就会允许任何加载的共享程序来执行可选的初始化代码。该函数允许库来初始化内部数据并备之待用。这个代码是在上述 ELF 映像的 .init 部分中定义的。在卸载库时,它还可以调用一个终止函数(定义为映像的 .fini 部分)。当初始化函数被调用时,动态链接器会把控制权转让给加载的原始映像。用 Linux 进行动态加载Linux 并不会自动为给定程序加载和链接库,而是与应用程序本身共享该控制权。这个过程就称为动态加载。使用动态加载,应用程序能够先指定要加载的库,然后将该库作为一个可执行文件来使用(即调用其中的函数)。但是正如您在前面所了解到的,用于动态加载的共享库与标准共享库(ELF 共享对象)无异。事实上,ld-linux 动态链接器作为 ELF 加载器和解释器,仍然会参与到这个过程中。动态加载(Dynamic Loading,DL)API 就是为了动态加载而存在的,它允许共享库对用户空间程序可用。尽管非常小,但是这个 API 提供了所有需要的东西,而且很多困难的工作是在后台完成的。表 1 展示了这个完整的 API。表 1. Dl API函数描述dlopen使对象文件可被程序访问dlsym获取执行了 dlopen 函数的对象文件中的符号的地址dlerror返回上一次出现错误的字符串错误dlclose关闭目标文件该过程首先是调用 dlopen,提供要访问的文件对象和模式。调用 dlopen 的结果是稍候要使用的对象的句柄。mode 参数通知动态链接器何时执行再定位。有两个可能的值。第一个是 RTLD_NOW,它表明动态链接器将会在调用 dlopen 时完成所有必要的再定位。第二个可选的模式是 RTLD_LAZY,它只在需要时执行再定位。这是通过在内部使用动态链接器重定向所有尚未再定位的请求来完成的。这样,动态链接器就能够在请求时知晓何时发生了新的引用,而且再定位可以正常进行。后面的调用无需重复再定位过程。还可以选择另外两种模式,它们可以按位 OR 到 mode 参数中。RTLD_LOCAL 表明其他任何对象都无法使加载的共享对象的符号用于再定位过程。如果这正是您想要的的话(例如,为了让共享的对象能够调用原始进程映像中的符号),那就使用 RTLD_GLOBAL 吧。dlopen 函数还会自动解析共享库中的依赖项。这样,如果您打开了一个依赖于其他共享库的对象,它就会自动加载它们。函数返回一个句柄,该句柄用于后续的 API 调用。dlopen 的原型为:#include &dlfcn.h&
void *dlopen( const char *file, int mode );有了 ELF 对象的句柄,就可以通过调用 dlsym 来识别这个对象内的符号的地址了。该函数采用一个符号名称,如对象内的一个函数的名称。返回值为对象符号的解析地址:void *dlsym( void *restrict handle, const char *restrict name );如果调用该 API 时发生了错误,可以使用 dlerror 函数返回一个表示此错误的人类可读的字符串。该函数没有参数,它会在发生前面的错误时返回一个字符串,在没有错误发生时返回 NULL:char *dlerror();最后,如果无需再调用共享对象的话,应用程序可以调用 dlclose 来通知操作系统不再需要句柄和对象引用了。它完全是按引用来计数的,所以同一个共享对象的多个用户相互间不会发生冲突(只要还有一个用户在使用它,它就会待在内存中)。任何通过已关闭的对象的 dlsym 解析的符号都将不再可用。char *dlclose( void *handle );动态加载示例了解了 API 之后,下面让我们来看一看 DL API 的例子。在这个应用程序中,您主要实现了一个 shell,它允许操作员来指定库、函数和参数。换句话说,也就是用户能够指定一个库并调用该库(先前未链接于该应用程序的)内的任意一个函数。首先使用 DL API 来解析该库中的函数,然后使用用户定义的参数(用来发送结果)来调用它。清单 2 展示了完整的应用程序。清单 2. 使用 DL API 的 Shell#include &stdio.h&
#include &dlfcn.h&
#include &string.h&
#define MAX_STRING
void invoke_method( char *lib, char *method, float argument )
float (*func)(float);
/* Open the shared object */
dl_handle = dlopen( lib, RTLD_LAZY );
if (!dl_handle) {
printf( "!!! %s\n", dlerror() );
/* Resolve the symbol (method) from the object */
func = dlsym( dl_handle, method );
error = dlerror();
if (error != NULL) {
printf( "!!! %s\n", error );
/* Call the resolved method and print the result */
%f\n", (*func)(argument) );
/* Close the object */
dlclose( dl_handle );
int main( int argc, char *argv[] )
char line[MAX_STRING+1];
char lib[MAX_STRING+1];
char method[MAX_STRING+1];
while (1) {
printf("& ");
line[0]=0;
fgets( line, MAX_STRING, stdin);
if (!strncmp(line, "bye", 3))
sscanf( line, "%s %s %f", lib, method, &argument);
invoke_method( lib, method, argument );
}要构建这个应用程序,需要通过 GNU Compiler Collection(GCC)使用如下的编译行。选项 -rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 来实现向后跟踪)。-ldl 表明一定要将 dllib 链接于该程序。gcc -rdynamic -o dl dl.c -ldl再回到 ,main 函数仅充当解释器,解析来自输入行的三个参数(库名、函数名和浮点参数)。如果出现 bye 的话,应用程序就会退出。否则的话,这三个参数就会传递给使用 DL API 的 invoke_method 函数。首先调用 dlopen 来访问目标文件。如果返回 NULL 句柄,表示无法找到对象,过程结束。否则的话,将会得到对象的一个句柄,可以进一步询问对象。然后使用 dlsym API 函数,尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针,或者是得到一个 NULL 并返回一个错误。在 ELF 对象中解析了符号后,下一步就只需要调用函数。要注意一下这个代码和前面讨论的动态链接的差别。在这个例子中,您强行将目标文件中的符号地址用作函数指针,然后调用它。而在前面的例子是将对象名作为函数,由动态链接器来确保符号指向正确的位置。虽然动态链接器能够为您做所有麻烦的工作,但这个方法会让您构建出极其动态的应用程序,它们可以再运行时被扩展。调用 ELF 对象中的目标函数后,通过调用 dlclose 来关闭对它的访问。清单 3 展示了一个如何使用这个测试程序的例子。在这个例子中,首先编译程序而后执行它。接着调用了 math 库(libm.so)中的几个函数。完成演示后,程序现在能够用动态加载来调用共享对象(库)中的任意函数了。这是一个很强大的功能,通过它还能够给程序扩充新的功能。清单 3. 使用简单的程序来调用库函数mtj@camus:~/dl$ gcc -rdynamic -o dl dl.c -ldl
mtj@camus:~/dl$ ./dl
& libm.so cosf 0.0
& libm.so sinf 0.0
& libm.so tanf 1.0
mtj@camus:~/dl$工具Linux 提供了很多种查看和解析 ELF 对象(包括共享库)的工具。其中最有用的一个当属 ldd 命令,您可以使用它来发送共享库依赖项。例如,在 dl 应用程序上使用 ldd 命令会显示如下内容:mtj@camus:~/dl$ ldd dl
linux-gate.so.1 =&
(0xffffe000)
libdl.so.2 =& /lib/tls/i686/cmov/libdl.so.2 (0xb7fdb000)
libc.so.6 =& /lib/tls/i686/cmov/libc.so.6 (0xb7eac000)
/lib/ld-linux.so.2 (0xb7fe7000)
mtj@camus:~/dl$ldd 所告诉您的是:该 ELF 映像依赖于 linux-gate.so(一个特殊的共享对象,它处理系统调用,它在文件系统中无关联文件)、libdl.so(DL API)、GNU C 库(libc.so)以及 Linux 动态加载器(因为它里面有共享库依赖项)。readelf 命令是一个有很多特性的实用程序,它让您能够解析和读取 ELF 对象。readelf 有一个有趣的用途,就是用来识别对象内可再定位的项。对于我们这个简单的程序来说( 展示的程序),您可以看到需要再定位的符号为:mtj@camus:~/dl$ readelf -r dl
Relocation section '.rel.dyn' at offset 0x520 contains 2 entries:
R_386_GLOB_DAT
__gmon_start__
001405 R_386_COPY
Relocation section '.rel.plt' at offset 0x530 contains 8 entries:
R_386_JUMP_SLOT
000607 R_386_JUMP_SLOT
000b07 R_386_JUMP_SLOT
000c07 R_386_JUMP_SLOT
__libc_start_main
00000e07 R_386_JUMP_SLOT
001007 R_386_JUMP_SLOT
001107 R_386_JUMP_SLOT
001907 R_386_JUMP_SLOT
mtj@camus:~/dl$从这个列表中,您可以看到各种各样的需要再定位(到 libc.so)的 C 库调用,包括对 DL API(libdl.so)的调用。函数
__libc_start_main 是一个
C 库函数,它优先于程序的
main 函数(一个提供必要初始化的 shell)而被调用。其他操作对象文件的实用程序包括:objdump,它展示了关于对象文件的信息;nm,它列出来自对象文件(包括调试信息)的符号。还可以将 EFL 程序作为参数,直接调用 Linux 动态链接器,从而手动启动映像:mtj@camus:~/dl$ /lib/ld-linux.so.2 ./dl
& libm.so expf 0.0
&另外,可以使用 ld-linux.so 的 --list 选项来罗列 ELF 映像的依赖项(ldd 命令也如此)。切记,它仅仅是一个用户空间程序,是由内核在需要时引导的。结束语本文只涉及到了动态链接器功能的皮毛而已。在下面的
中,您可以找到对 ELF 映像格式和过程或符号再定位的更详细的介绍。而且和 Linux 其他所有工具一样,你也可以下载动态链接器的源代码(参见 )来深入研究它的内部。
相关主题 您可以参阅本文在 developerWorks 全球站点上的 。 查看 Peter Seebach 所著的文章 “”(developerWorks,2005 年 1 月),了解共享库,学习如何构建共享库,并了解可以用于剖析共享库的各种工具。从 Debian 下载
。这是关于动态链接和动态加载信息的最基本的信息来源。 提供了对
(PDF),涵盖了对对象文件、程序加载和 C 库的介绍。Wikipedia 还提供了对
以及很多介绍 ELF 其他信息(很多处理器架构的规格和接口)的资源链接。
Chris Rohlf 的
详细描述了 ELF 符号解析和所有具体细节。它解释了 GOT 和 PLT 表格以及如何通过 Linux 动态链接器来操作它们。 Wikipedia 有很多关于
在那里,您还可以学习到有关
的知识,以及它们与库的关系。 阅读
“”(developerWorks,2005 年 12 月),这是一篇很好的对 ELF 的介绍。ELF 是 Linux 的标准对象格式。ELF 是一个灵活的文件格式,它涵盖了有关可执行映像、对象、共享库乃至内核转储的信息。在
中查找更详细的信息。Linux Journal 文章
“”(2002 年 11 月)很好地介绍了链接器和加载器使用 ELF 文件(包括符号解析和再定位)的背后意图。在
中可以找到为 Linux 开发人员准备的更多参考资料,还可以查阅
。在 developerWorks 上查阅所有
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=http://www.ibm.com/developerworks/js/artrating/SITE_ID=10Zone=LinuxArticleID=336201ArticleTitle=Linux 动态库剖析publish-date=linux环境下: 静态连接库,动态连接库, 甚至直接的源代码编译单元的优缺点
[问题点数:80分,结帖人redex]
linux环境下: 静态连接库,动态连接库, 甚至直接的源代码编译单元的优缺点
[问题点数:80分,结帖人redex]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2008年9月 C/C++大版内专家分月排行榜第二2008年4月 C/C++大版内专家分月排行榜第二2008年2月 C/C++大版内专家分月排行榜第二2007年7月 C/C++大版内专家分月排行榜第二
2011年10月 C/C++大版内专家分月排行榜第三2009年1月 C/C++大版内专家分月排行榜第三2008年10月 C/C++大版内专家分月排行榜第三2008年5月 C/C++大版内专家分月排行榜第三2007年6月 C/C++大版内专家分月排行榜第三2007年5月 C/C++大版内专家分月排行榜第三
2007年6月 总版技术专家分月排行榜第三
2007年6月 VC/MFC大版内专家分月排行榜第一
2007年12月 C/C++大版内专家分月排行榜第二2006年11月 C/C++大版内专家分月排行榜第二
2004年3月 C/C++大版内专家分月排行榜第三
匿名用户不能发表回复!|}

我要回帖

更多关于 linux 特点 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信