您的位置:首页 > 运维架构 > Linux

GCC与静态库、共享库以及动态加载库

2013-09-06 11:52 260 查看

1.      GCC中不同类型的文件:

后缀
内容
.a
 静态对象库文件
.i
已经进行预处理的C源文件
.o
对象文件(-c)
.s
汇编语言代码(-S)
.so
共享对象库文件
 

2.      常见命令行选项与结果:

命令行
结果
gcc helloworld.c
默认的gcc行为是将源文件编译生成对象文件,并连接并产生一个可执行文件,最后删除中间产生的对象文件。产生可执行文件,不过名称为默认的a.out.
gcc helloworld.c –o helloworld
-o选项用来指定最后产生的可执行文件名
gcc -c helloworld.c
默认产生文件名为helloworld.o的对象文件,同样可以用-o选项指定对象文件名;值得注意的是,在一条命令中可以同时产生多个对象。
gcc hellomain.c sayhello.c -o hello
将相互存在依赖关系的文件通过编译链接最后生成可执行文件,以本例来讲,前者依赖后者。
gcc -E helloworld.c
产生预处理代码,默认情况下输出到标准输出,同样可以通过-o选项指定输出到哪个文件中,如helloworld.i
gcc -S helloworld.c
产生汇编语言代码
 

3.      静态库、共享库以及动态加载库

(1) 静态库

静态库是一些目标文件的集合,库文件以”.a”结尾,连接器会将应用程序所需要用到的代码拷贝到应用程序中.



【如何创建与使用静态库】

a>    首先生成目标文件: gcc -Wall -cmax.c

b>    创建静态库:ar rcs libmax.a max.o (可以根据ar –tlibmax.a查看静态库中所包含的目标文件)

c>    使用静态库:gcc hello_world.c libmax.a-o hello_world 或

gcc hello_world.c –L. –lmax -o hello_world

在使用静态库的过程中,可以指定静态库的全名,或者调用-l选项指定静态库的缩写名(静态库全名一般是以lib开头,后面跟静态库的缩写名),但是要注意的是,在这种情况下,最好是自己通过-L选项增加库搜索路径,否则编译的时候可能会出现库搜索不到的错误。另外一点,-l选项必须放在需要编译的文件之后,否则也会出现编译错误!
d>    静态库的优缺点:
静态库增加了应用程序的大小,另外在处理静态库更新问题上需要花费更多的重编译代价(recompile),但是理论上,静态库应该比共享库或者动态加载库运行更快(1-5%),因为它减少了在程序运行才去加载库的开销。

(2)  共享库:

       与静态库不同的是,共享库在链接阶段并不需要拷贝所需使用的代码,而只是做些参考标记,然后在程序启动时加载所需要的库文件,因此,对比静态库,链接共享库的应用程序小得多。



【共享库的名字】
       跟共享库相关的名字有三个,分别是soname, real name和linker name,其中soname一般用来提供版本的兼容相关信息,real name则是包含实际共享库代码的文件名,linker name则是用于应用程序链接时候的一个搜索名。一般来讲,soname的命名以lib作为前缀,后者则是库名,接着是”.so”,最后则包含着周期数以及递增的版本信息,它一般作为real
name的符号链接;real name则是在soname的基础上加上一个周期数以及最小版本信息,和另一个周期数以及发行版本号(releasenumber).linker name则是在soname的基础上去掉了版本号,linker name是soname的符号链接。
 【如何创建与使用共享库】
a>    首先创建对象文件:gcc -Wall -fpic-c max.c -fpic选项是一种编译器标志,表示产生与内存位置无关的代码,即在产生代码中的过程中全部使用相对地址,而不要使用绝对         地址,其中pic代表的是position independent code.
b>    创建共享库:
gcc -shared -Wl,-soname,libmax.so.1-olibmax.so.1.0 max.o
mv libmax.so.1.0 /opt/lib
ln -sf /opt/lib/libmax.so.1.0/opt/lib/libmax.so.1 (这条语句可以有ldonfig–n dir命令来完成)
ln -sf /opt/lib/libmax.so.1 /opt/lib/libmax.so
-shared选项是一种编译器标志,用来创建共享库,soname选项则是链接器标志,用来生成soname,在本例中,libmax.so.1是soname, libmax.so.1.0则是real name, libmax.so则是linker name.
【引申】共享库命名与版本兼容问题:
       以上面命名为例,若某个API发生改变,则所创建共享库的realname的主版本号应该发生改变,如此时,变成libmax.so.2.0, 若只是对函数升级了函数库而功能没有发生改变,则只需改变次版本号,如此时变成libmax.so.1.1, 这样可以做到版本兼容.
 c> 使用共享库:
       gcc -Wall -L/opt/lib hello_word.c -lmax-ohello_world
       使用方法类似于静态库,但是在应用程序运行的时候,却有着本质的区别,例如如果我们现在运行此时生成的应用程序hello_world,则会出现下图的错误信息,那是什么原因呢?正如前面所讲,程序在运行时还得需要加载所需要的库文件,此时不能找到库文件路径,处理这种错误可以采取以下两个方法:



设置LD_LIBRARY_PATH环境变量,可以用来配置共享库的搜索路径:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH(将当前路径加入搜索目录)
配置-rpath编译选项用来表示增加一个目录到运行时库中,具体方法如下:
       gcc -shared-Wl,-soname,libmax.so.1,-rpath . -o libmax.so.1.0 max.o
d> 共享库的优缺点:
       对比静态库,共享库在链接阶段只是对所需代码做些标识,在程序启动时才会加载,这样减少了目标应用程序的大小,通过soname,可以做到多版本的兼容,这样每次升级也不需要将原代码全部重新编译,免除了在升级过程中重编译带来的开销;但是,因为需要在程序运行阶段加载共享库,这样势必需要付出运行期间的库加载代价。
e>    共享库相关的工具:
ldd name-of-executable  显示执行文件相依赖的共享库
nm name-of-library 显示库(静态或者动态)相关的标识符(symbol)

(3)动态加载库 (Dynamically loaded libraries)

       是指在程序运行过程中可以加载的函数库,而不像共享库是在程序启动的时候加载。DLL对实现插件和模块非常实用,因为它们运行程序在允许时等待插件的加载,动态加载库有自己的一套API接口去完成打开、查找符号,处理出错、关闭加载库等功能。
       void*dlopen (const char *libname, int flag);
       dlopen必须在dlerror, dlsym以及dlclose函数之前调用,表示要将共享库加载到内存中。若所加载的共享库还需要依赖其它库,则必须首先加载依赖库。若dlopen操作失败,则返回NULL,若库已经被加载过,则会返回同样的函数句柄,参数libname一般指库的全路径,这样直接加载库文件,若只是指定了库文件名,则会按照以下顺序进行库的查找:
a.      根据环境变量LD_LIBRARY_PATH查找;
b.      根据/etc/ld/so.cache目录查找;
c.      依次在/lib和/usr/lib目录查找;
         flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW,前者表示暂时不处理未定义的函数,先把库加载在内存中,等遇到未定义的函数再说;后者表示立马检查是否存在未定义的函数,若存在,则以失败告终。
       void*dlsym(void *handle, const char *symbol);
       dlsym可以获得指定函数(根据symbol)在内存中的位置(指针)。若找不到函数,则返回NULL。
       Int dlclose(void *);
       dlclose可以关闭一个已经加载的库,若存在析构函数,则析构函数会调用。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux gcc