通过例子说明静态库、动态库以及链接和运行时库:
有如下的目录:
hejinxin@google:~/Mytest$ ls project/
lib1 lib2 test.c
hejinxin@google:~/Mytest$ cd project/
hejinxin@google:~/Mytest/project$ ls lib*
lib1:
say1.c
lib2:
say2.c
在project目录下有lib1、lib2子目录和文件test.c,lib1和lib2下面分别有文件say1.c和say2.c,内容如下:
/*************************************************************************
> File Name: say1.c
> Author: jxhe
> Created Time: 2015年01月22日 星期四 17时55分16秒
************************************************************************/
#include<stdio.h>
void say()
{
printf("Say1!");
}
/*************************************************************************
> File Name: say2.c
> Author: jxhe
> Created Time: 2015年01月22日 星期四 17时55分16秒
************************************************************************/
#include<stdio.h>
void say()
{
printf("Say2!");
}
在project下面的test.c是调用lib1中的say()函数或者lib2中的say()。
/*************************************************************************
> File Name: test.c
> Author: jxhe
> Created Time: 2015年01月22日 星期四 17时57分08秒
************************************************************************/
#include<stdio.h>
int main()
{
say();
return 0;
}
下面分别在lib1和lib2下编译.c文件,但是都生成同一个库名。
hejinxin@google:~/Mytest/project$ cd lib1/
hejinxin@google:~/Mytest/project/lib1$ ls
say1.c
hejinxin@google:~/Mytest/project/lib1$ gcc -o libsay.so -fPIC -shared say1.c
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so say1.c
文件say2.c也作同样的编译。现在通过指定共享库的路径,编译test可执行文件。
hejinxin@google:~/Mytest/project/lib1$ cd ..
hejinxin@google:~/Mytest/project$ ls
lib1 lib2 test.c
hejinxin@google:~/Mytest/project$ gcc -o test -Llib1 -lsay test.c
hejinxin@google:~/Mytest/project$ ls
lib1 lib2 test test.c
hejinxin@google:~/Mytest/project$ ./test
./test: error while loading shared libraries: libsay.so: cannot open shared object file: No such file or directory
hejinxin@google:~/Mytest/project$ ldd test
linux-vdso.so.1 => (0x00007fff54945000)
libsay.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd5eb7e6000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd5ebbbf000)
在执行test程序是报错,说没有libsay这个共享库。但是我们知道在编译是通过-L指定库在lib1下面,为什么ldd工具分析得出的结论是not found?这是因为程序执行时,查找库的路径和编译时指定的不同。有几种方法能修补现在的错误。一是,将包含目标共享库的路径添加到/etc/ld.so.conf,然后执行ldconfig。ldconfig的作用是读取ld.so.conf的内容到ld.so.cache中,然后程序执行中遇到调用共享库的函数时,就按照这个文件中提供的路径去查找;第二个方法是,将共享库所在的路径添加到环境变量LD_LIBRARY_PATH中。
hejinxin@google:~/Mytest/project$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/home/hejinxin/Mytest/project/lib1
在加入到ld.so.conf之后,再调用ldd和执行程序查看结果。
hejinxin@google:~/Mytest</project$ ldd test
linux-vdso.so.1 => (0x00007fff792aa000)
libsay.so => /home/hejinxin/Mytarpackeg/project/lib1/libsay.so (0x00007fb43ecda000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb43e91b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb43eef4000)
hejinxin@google:~/Mytest/project$ ./test
Say1!
可以看到效果了,libsay.so => 指向的地址正是这个共享库的目录,而不再是not found。test执行的结果是Say1!。那么,如果我们需要更新共享库,而不想重新编译整个程序时,怎么办呢?上例已经做了提前安排,就是lib2里也有一个同名的共享库,且函数接口也相同,只是函数内容不同。可以把lib2里的函数拷贝到lib1里,覆盖原先的库函数,看看执行结果:
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so.bak say1.a say1.c say1.o
hejinxin@google:~/Mytest/project/lib1$ cp ../lib2/libsay.so .
hejinxin@google:~/Mytest/project/lib1$ ls
libsay.so libsay.so.bak say1.a say1.c say1.o
hejinxin@google:~/Mytest/project/lib1$ cd ..
hejinxin@google:~/Mytest/project$ ls
lib1 lib2 test test.c
hejinxin@google:~/Mytest/project$ ./test
Say2!
结果输出的是Say2!,说明共享库更新成功!
1、什么是共享库
共享库的代码是可以在多个应用程序之间共享的,也就是如果有多个程序(或者叫进程)调用相同的函数,可以把这些共同的函数提取出来制作成共享库。这样在运行时,内存中只需要拷贝一份就可以了。
2、共享库与静态库的区别
静态库就是目标文件的简单打包,在编译过程中调用静态库,就会将涉及到的目标文件拷贝进可执行文件中,然后链接程序进行重定位。此后,程序的运行、调试都不再需要静态库了。如果每个可执行文件都拷贝一些基础函数或者目标文件,会有严重的内存空间浪费。同时,共享库在软件更新时也有优势,只要保证接口不变,用新的同名共享库覆盖原有的库就实现了软件更新。
3、如何编译共享库
编译共享库需要给gcc指定相关的设置,-fPIC -shared,PIC是position independent code的缩写,就是位置无关的代码;-shared表明代码是共享的。
4、链接过程是怎样的
程序编译的最后一个过程就是链接,链接器ld负责将多个目标文件链接起来,修改它们中的重定位表,使得可重定位符号指定到特定位置。
5、运行时如何操作
在程序运行时,系统会首先判断这个程序是动态链接的还是静态链接的。如果是动态链接的,运行时动态连接器ld-linux.so检查程序依赖的共享库,并判断是否已经在内存中?如果不在,加载这些库绑定重定位的符号。这里就有一个问题了,运行时动态连接器是如何找到共享库?会首先查找/etc/ld.so.cache,如果没有这个库的路径,就程序就无法运行。而ld.so.cache其实是通过ldconfig程序读取/etc/ld.so.conf文件的内容,因此,安装库后应该把库文件的路径加入到/etc/ld.so.conf文件里面。