GCC lib分类及优缺点

来源:互联网 发布:淘宝上有正宗沉香卖吗 编辑:程序博客网 时间:2024/04/24 10:20
 静态函数库(static libraries)
 静态函数库是在程序执行前就加入到目标程序中去了
 静态函数库实际上就是简单的一个普通的目标文件的集合,一般来说习惯用".a"作为文件的后缀。
 可以用ar这个程序来产生静态函数库文件。Ar 是archiver的缩写。
 静态库函数允许程序员把程序link起来而不用重新编译代码,节省了重新编译代码的时间
 静态函数库对开发者来说还是很有用的,例如你想把自己提供的函数给别人使用,
 但是又想对函数的源代码进行保密,你就可以给别人提供一个静态函数库文件。
 理论上说,使用ELF格式的静态库函数生成的代码可以比使用共享函数库(或者动态函数 库)的程序运行速度上快一些,
 大概1-5%。
 创建一个静态函数库文件,或者往一个已经存在地静态函数库文件添加新的目标代码,可以用下面的命令:
 ar rcs my_library.a file1.o file2.o 把目标代码file1.o和file2.o加入到my_library.a这个函数库文件中
 可以用“-l”参数来指定这个库函数。你也可以用ld来做,使用它的“-l”和“-L”参数选项
 
 
共享函数库(shared libraries)
 共享函数库则是在程序启动的时候加载到程序中,它可以被不同的程序共享
 共享函数库中的函数是在当一个可执行程序在启动的时候被加载。
 如果一个共享函数库正常安装,所有的程序在重新运行的时候都可以自动加载最新的函数库中的函数。
 o 升级了函数库但是仍然允许程序使用老版本的函数库。
 o 当执行某个特定程序的时候可以覆盖某个特定的库或者库中指定的函数。
 o 可以在库函数被使用的过程中修改这些函数库。
 Soname名字命名必须以“lib”作为前缀,然后是函数库的名字,然后是“.so”,最后是版本号信息
 /usr/lib/libreadline.so.3 是一个完全的完整的soname,ldconfig可以设置一个符号链接到其他某个真正的函数库文件,
 例如是 /usr/lib/libreadline.so.3.0。同时还必须有一个链接名字,
 例如/usr/lib/libreadline.so 就是一个符号链接指向/usr/lib/libreadline.so.3。
 大部分的函数库文件应该安装到/usr/lib目录 下,
 如果某些库是在系统启动的时候要加载的,则放到/lib目录下,
 而那些不是系统本身一部分的库则放到/usr/local/lib下面
 具体的位置信息可以看/etc/ld.so.conf里面的配置信息。
 下面再给一个例子,它创建两个object文件(a.o和b.o),然后创建一个包含a.o和b.o的共享函数库。例子中”-g”和“-Wall”参数不是必须的。
 gcc -fPIC -g -c -Wall a.c
 gcc -fPIC -g -c -Wall b.c
 gcc -shared -Wl,-soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc
 安装:ldconfig -n directory_with_shared_libraries
 
 
动态加载函数库(dynamically loaded libraries)。
 动态加载函数库则可以在程序运行的任何时候动态的加载。实际上,动态函数库并非另外一种库函数格式,
 区别是动态加载函数库是如何被程序员使用的。
 Linux中使用的函数和Solaris中一样,都是dlpoen() API。当时不是所有的平台都使用同样的接口,
  例如HP-UX使用shl_load()机制,而Windows平台用另外的其他的调用接口。
  如果你的目的 是使得你的代码有很强的移植性,你应该使用一些wrapping函数库,
  这样的wrapping函数库隐藏不同的平台的接口区别。一种方法是使用 glibc函数库中的对动态加载模块的支持,
  它使用一些潜在的动态加载函数库界面使得它们可以夸平台使用。
  具体可以参考http: //developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html.
  另外一个方法是使用libltdl,是GNU libtool的一部分,可以进一步参考CORBA相关资料。
 void * dlopen(const char *filename, int flag);
 就会按照下面的次序查找函数库文件:
 1. 环境变量LD_LIBRARY指明的路径。
 2. /etc/ld.so.cache中的函数库列表。
 3. /lib目录,然后/usr/lib。不过一些很老的a.out的loader则是采用相反的次序,也就是先查/usr/lib,然后是/lib。
 参数flag的值必须是RTLD_LAZY或者RTLD_NOW,
 RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,
 而RTLD_NOW的含义是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'
 如果有好几个函数库,它们之间有一些依赖关系的话,例如X依赖Y,那么你就要先加载那些被依赖的函数。例如先加载Y,然后加载X。
  dlopen()函数的返回值是一个句柄,然后后面的函数就通过使用这个句柄来做进一步的操作。如果打开失败dlopen()就返回一个NULL。如果一个函数库被多次打开,它会返回同样的句柄
 如果一个函数库里面有一个输出的函数名字为_init,那么_init就会在dlopen()这个函数返回前被执行。我们可以利用这个函数在我的函数库里面做一些初始化的工作。我们后面会继续讨论这个问题的
 
 
通过调用dlerror()函数,我们可以获得最后一次调用dlopen(),dlsym(),或者dlclose()的错误信息。 
void * dlsym(void *handle, char *symbol);
   函数中的参数handle就是由dlopen打开后返回的句柄,symbol是一个以NIL结尾的字符串。
   如果dlsym()函数没有找到需要查找的symbol,则返回NULL。如果你知道某个symbol的值不可能是NULL或者0,那么就很好,
 你就可以根据这个返回结果判断查找的symbol是否存在了;不过,如果某个symbol的值就是NULL,那么这个判断就有问题了。
 标准的判断方法 是先调用dlerror(),清除以前可能存在的错误,然后调用dlsym()来访问一个symbol,
 然后再调用dlerror()来判断是否出现了错 误。一个典型的过程如下: 
 dlerror(); /* clear error code */
 s = (actual_type) dlsym(handle, symbol_being_searched_for);
 if ((err = dlerror()) != NULL)
 {
  /* handle error, the symbol wasn't found */
 }
 else
 {
  /* symbol found, its value is in s */
 }
 dlopen()函数的反过程就是dlclose()函数,dlclose()函数用力关闭一个DL函数库。Dl函数库维持一个资源利用的计数 器,当调用dlclose的时候,就把这个计数器的计数减一,如果计数器为0,则真正的释放掉。真正释放的时候,如果函数库里面有_fini()这个函 数,则自动调用_fini()这个函数,做一些必要的处理。Dlclose()返回0表示成功,其他非0值表示错误。
 
 
 int main(int argc, char **argv)
   {
     void *handle;
     double (*cosine)(double);
     char *error;
     handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
     if (!handle) {
         fputs (dlerror(), stderr);
         exit(1);
     }
     cosine = dlsym(handle, "cos");
     if ((error = dlerror()) != NULL)
   {
         fputs(error, stderr);
         exit(1);
     }
     printf ("%f ", (*cosine)(2.0));
     dlclose(handle);
 }
   如果这个程序名字叫foo.c,那么用下面的命令来编译:
   gcc -o foo foo.c -ldl
  
 
 
 nm命令可以列出一个函数库文件中的符号表。它对于静态的函数库和共享的函数库都起作用。
 类型有一下几种:T、D、B、U、W。各自的含义如下:
  T表示在代码段中定义的一般变量 符号;
  D表示时初始化过的数据段;
  B表示初始化的数据段;
  U表示没有定义的,在这个库里面使用了,但是在其他库中定义的符号;
  W,weak的缩写,表示如 果其他函数库中也有对这个符号的定义,则其他符号的定义可以覆盖这个定义。
  
 可以用mn的“-o”选项和grep命令来查找库的名字
  nm -o /lib/* /usr/lib/* /usr/lib/*/* /usr/local/lib/* 2> /dev/null | grep 'cos$'
  
 函数库里面有两个特殊的函数,_init和_fini,这个我们在前面已经说过了。
 主要是分别用来初始化函数库和关闭的时候做一些必要的处理, 我们可以把自己认为需要的代码放到这两个函数里面,
 它们分别在函数库被加载和释放的时候被执行。具体说,如果一个函数库里面有一个名字为“_init”的 函数输出,
 那么在第一次通过dlopen()函数打开这个函数库,或者只是简单的作为共享函数库被打开的时候,_init函数被自动调用执行。
 与之相对应 的就是_fini函数,当一个程序调用dlclose()去释放对这个函数库的引用的时候,
 如果该函数库的被引用计数器为0了,或者这个函数库是作为一般 的共享函数库被使用而使用它的程序正常退出的时候,
 _fini就会被调用执行。C语言定义它们的原型如下:
 void _init(void); void _fini(void);
 当用gcc编译源程序为“.o”文件的时候,需要加一个“-nostartfiles”选项。这个选项使得C编译器不链接系统的启动函数库里面的启动函数。否则,就会得到一个“multiple-definition”的错误。
 
  
 
原创粉丝点击