一、静态库与动态库
1. 静态库
静态库其实就是商业公司将自己的函数库源代码经过只编译不连接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件(.a的归档文件又叫静态链接库文件)。商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用;客户拿到.a和.h文件后,通过.h头文件得知库中的库函数的原型,然后在自己的.c文件中直接调用这些库文件,在连接的时候链接器会去.a文件中拿出被调用的那个函数的编译后的.o二进制代码段链接进去形成最终的可执行程序。
2. 动态链接库
动态链接库比静态链接库出现的晚一些,效率更高一些,是改进型的。现在我们一般都是使用动态库。
静态库在用户链接自己的可执行程序时就已经把调用的库中的函数的代码段链接进最终可执行程序中了,这样好处是可以执行,坏处是太占地方了。尤其是有多个应用程序都使用了这个库函数时,实际上在多个应用程序最后生成的可执行程序中都各自有一份这个库函数的代码段。当这些应用程序同时在内存中运行时,实际上在内存中有多个这个库函数的代码段,这完全重复了。
而动态链接库本身不将库函数的代码段链接入可执行程序,只是做个标记。然后当应用程序在内存中执行时,运行时环境发现它调用了一个动态库中的库函数时,会去加载这个动态库到内存中,然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行(不会重复加载)。
静态库链接生成的文件,在内存中调用执行时,不需要库函数,自己能够独立运行。而动态链接生成的文件执行时,要加载库函数到内存中,依赖库函数。
二. 使用库函数
(1) gcc中编译链接程序默认是使用动态库的,要想静态链接需要显式用-static来强制静态链接。
root@ubuntu:/mnt/hgfs/winshare/test# gcc -o test1 test1.c
root@ubuntu:/mnt/hgfs/winshare/test# ls -al test1
-rwxrwxrwx 1 root root 7497 Apr 20 15:05 test1
使用静态链接 -static
root@ubuntu:/mnt/hgfs/winshare/test# gcc -o test1 test1.c -static
root@ubuntu:/mnt/hgfs/winshare/test# ls -al test1
-rwxrwxrwx 1 root root 737836 Apr 20 15:05 test1
链接时如果没有指定-static,编译器会先去找.so动态库进行动态链接。如果找不到.so,再去找.a进行静态链接。
root@ubuntu:/mnt/hgfs/winshare/test# ls
111 makefile socketpair.c test1so testc timerfd.c
libtest.a pipe test test2 test.c unix_client.c
main pipe.c test1.c test2.c test.h unix_server.c
main.c socketp test1.h test2.h test.o
一开始只有静态库.a,进行了静态链接
root@ubuntu:/mnt/hgfs/winshare/test# gcc -o test1 test1.c -ltest -L.
root@ubuntu:/mnt/hgfs/winshare/test# ls -al test1
-rwxrwxrwx 1 root root 7382 Apr 20 16:53 test1
root@ubuntu:/mnt/hgfs/winshare/test# cp ../libtest.so ./
后来有了.a 和 .so,进行了动态链接
root@ubuntu:/mnt/hgfs/winshare/test# ls
111 makefile test test2 test.h
libtest.a pipe test1 test2.c test.o
libtest.so pipe.c test1.c test2.h timerfd.c
main socketp test1.h testc unix_client.c
main.c socketpair.c test1so test.c unix_server.c
root@ubuntu:/mnt/hgfs/winshare/test# gcc -o test1 test1.c -ltest -L.
root@ubuntu:/mnt/hgfs/winshare/test# ls -al test1
-rwxrwxrwx 1 root root 7327 Apr 20 16:54 test1
(2) 库函数的使用需要注意3点:
第一,包含相应的头文件;
第二,调用库函数时注意函数原型;
第三,有些库函数链接时需要额外用-lxxx来指定链接;
第四,如果是动态库,要注意-L指定动态库的地址。
(3) 使用数学库
使用库函数:double sqrt(double x);
注意区分编译时警告/错误,和链接时的错误:
编译时警告/错误:
4.6.10.math.c:9:13: warning: incompatible implicit declaration of built-in function ‘sqrt’ [enabled by default] double b = sqrt(a);
链接时错误:
4.6.10.math.c:(.text+0x1b): undefined reference to `sqrt’ collect2: error: ld returned 1 exit status
分析:
这个链接错误的意思是:sqrt函数有声明(声明就在math.h中)有引用(在math.c)但是没有定义,链接器找不到函数体。sqrt本来是库函数,在编译器库中是有.a和.so链接库的(函数体在链接库中的)。
C链接器的工作特点:因为库函数有很多,链接器去库函数目录搜索的时间比较久。为了提升速度想了一个折中的方案:链接器只是默认的寻找几个最常用的库,如果是一些不常用的库中的函数被调用,需要程序员在链接时明确给出要扩展查找的库的名字。链接时可以用-lxxx来指示链接器去到libxxx.so中去查找这个函数。
解决办法:
链接时加-lm
-lm就是告诉链接器到libm中去查找用到的函数。
三. 制作静态库并使用
1. 编写.c .h文件
2. 编译成.a库文件
首先使用gcc -c只编译不连接,生成.o文件;
gcc -o test.o -c test.c
然后使用ar工具进行打包成.a归档文件
ar -rc libtest.a test.o
root@ubuntu:/mnt/hgfs/winshare/test# gcc -o test.o -c test.c
root@ubuntu:/mnt/hgfs/winshare/test# ar -rc libtest test.o
root@ubuntu:/mnt/hgfs/winshare/test# ls -al libtest
-rwxrwxrwx 1 root root 1176 Apr 20 15:41 libtest
库名不能随便乱起,一般是lib+库名称,后缀名是.a表示是一个归档文件
注意:制作出来了静态库之后,发布时需要发布.a文件和.h文件。
3. 使用静态链接库
把.a和.h都放在我引用的文件夹下,然后在.c文件中包含库的.h,然后直接使用库函数。需要使用-ltest -L .
第一次,编译方法:gcc test.c -o test
报错信息:test.c:(.text+0xa): undefined reference tofunc1' test.c:(.text+0x1e): undefined reference to
func2’
第二次,编译方法:gcc test.c -o test -laston
报错信息:/usr/bin/ld: cannot find -laston
collect2: error: ld returned 1 exit status
第三次,编译方法:gcc test.c -o test -laston -L.
无报错,生成test,执行正确。
除了ar名另外,还有个nm命令也很有用,它可以用来查看一个.a文件中都有哪些符号
四. 制作动态链接库
1. 制作动态链接库
(1) 只编译不链接,生成.o二进制文件
gcc -o test.o -c test.c -fPIC
(2) 使用gcc命令生成.so文件
gcc -o libtest.so test.o -shared
-fPIC是位置无关码,-shared是按照共享库的方式来链接。
注意:做库的人给用库的人发布库时,发布libxxx.so和xxx.h即可。
2. 使用自己创建的共享库。
(1) 编译
第一步,编译方法:gcc test.c -o test
报错信息:test.c:(.text+0xa): undefined reference to func1' test.c:(.text+0x1e): undefined reference to
func2’
collect2: error: ld returned 1 exit status
第二步,编译方法:gcc test.c -o test -laston
报错信息:/usr/bin/ld: cannot find -laston
collect2: error: ld returned 1 exit status
第三步,编译方法:gcc test.c -o test -laston -L.
编译成功
(2) 运行
编译成功,但是运行出错,报错信息:
error while loading shared libraries: libaston.so: cannot open shared object file: No such file or directory
错误原因:动态链接库运行时需要被加载(运行时环境在执行test程序的时候发现他动态链接了libaston.so,于是乎会去固定目录尝试加载libaston.so,如果加载失败则会打印以上错误信息。)
解决方法一:将libaston.so放到固定目录下就可以了
这个固定目录一般是/usr/lib目录。
cp libaston.so /usr/lib即可
root@ubuntu:/mnt/hgfs/winshare/test# ./test1
./test1: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
root@ubuntu:/mnt/hgfs/winshare/test# cp libtest.so /usr/lib/
root@ubuntu:/mnt/hgfs/winshare/test#
root@ubuntu:/mnt/hgfs/winshare/test# ./test1
my func test, ha ha ha
解决方法二:使用环境变量LD_LIBRARY_PATH
操作系统在加载固定目录/usr/lib之前,会先去LD_LIBRARY_PATH这个环境变量所指定的目录下去寻找,如果找到就不用去/usr/lib下面找了,如果没找到再去/usr/lib下面找。所以解决方案就是将libaston.so所在的目录导出到环境变量LD_LIBRARY_PATH中即可。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/Winshare/s5pv210/AdvancedC/4.6.PreprocessFunction/4.6.12.sharedobject.c/sotest
root@ubuntu:/mnt/hgfs/winshare/test# ./test1
./test1: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
root@ubuntu:/mnt/hgfs/winshare/test# echo $LD_LIBRARY_PATH //原来是空
root@ubuntu:/mnt/hgfs/winshare/test# pwd
/mnt/hgfs/winshare/test
root@ubuntu:/mnt/hgfs/winshare/test# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/winshare/test
root@ubuntu:/mnt/hgfs/winshare/test# echo $LD_LIBRARY_PATH
:/mnt/hgfs/winshare/test
root@ubuntu:/mnt/hgfs/winshare/test# ./test1
my func test, ha ha ha
在ubuntu中还有个解决方案三,用ldconfig,运行时除了到/usr/lib下找,还会到ldconfig指定的目录下去找。
3. ldd命令
作用是可以在一个使用了共享库的程序执行之前解析出这个程序使用了哪些共享库,并且查看这些共享库是否能被找到,能被解析(决定这个程序是否能正确执行)。
root@ubuntu:/mnt/hgfs/winshare/test# ldd test1
linux-gate.so.1 => (0xb770a000)
libtest.so (0xb7704000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7540000)
/lib/ld-linux.so.2 (0xb770b000)
root@ubuntu:/mnt/hgfs/winshare/test# export LD_LIBRARY_PATH=
root@ubuntu:/mnt/hgfs/winshare/test# echo $LD_LIBRARY_PATH
root@ubuntu:/mnt/hgfs/winshare/test# ldd test1
linux-gate.so.1 => (0xb77ab000)
libtest.so => not found
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75e4000)
/lib/ld-linux.so.2 (0xb77ac000)