Day4
库的概念,特点以及分类
库的概念:库其实就是一个二进制文件,里面包含的都是一些已经编译好的代码,可以直接供函数调用
根据库的功能,可以划分为:标准C库、数学库、线程库等等
系统默认的库的搜索路径为:/lib or /usr/lib
库的特点
- 库是事先编译好的,可以复用的代码
- 在OS上运行的程序基本都需要库。使用库可以提高开发效率
- 不同的操作系统下,库的文件格式不兼容,例如Windows 和 Linux下的库文件的格式就不兼容
- Linux下包含静态库和共享库。
静态库
特点:使用到静态库时,在编译(链接时)把静态库中的相关代码复制到可执行文件中
优点:程序中已包含代码,运行时不再需要加载库,运行速度更快
缺点:
- 由于静态库中的文件是直接复制到可执行程序中,所以占用更多的磁盘空间,运行时占的内存更大
- 静态库升级后,原来用到静态库的程序都需要重新编译链接
静态库的创建及使用
- 首先确定库中函数的功能和接口
- 编写库源码 hello.c bye.c
- 分别编译生成目标文件
gcc -c hello.c -o hello
- 创建静态库 libstatic.a
ar crs libstatic.a hello.o bye.o
其中:ar 命令,专门用来生成静态库文件,crs 是对应的选项,libstatic.a是生成的库文件的名称,Linux中要求库的名称 以lib 开头+库名称+.a ;后面的hello.o bye.o 即为要添加到库中的目标文件 - 编写应用程序 test.c (在此程序中 调用静态库中的函数 hello() 和 bye() )
- 编译test.c 并链接静态库 libhello.a
gcc test.c -o test -L. -lstatic
这里的 -L 选项是 指定添加一个库的搜索路径 . 代表当前路径;因为库一般是调用系统中的lib库中的函数,一般不会搜索当前路径的库,因此在这个示例中,由于我们是在当前路径下创建的一个静态库,因此要用到-L 命令添加一个新的库的搜索路径, -l 指定库的名称 static,这里注意不是库文件名libstatic.a - 执行程序,观察结果
./test
就可以出结果,不用再加载库 - 查看静态库中的信息
总之 先编译函数生成目标文件 --> 生成静态库 --> 编写程序,用到了静态库中的函数 --> 编译、链接静态库 --> 执行
示例如下:
linux@linux:~/test/lv4/static$ vi hello.c
linux@linux:~/test/lv4/static$ gcc -c hello.c -o hello.o
linux@linux:~/test/lv4/static$ vi name.c
linux@linux:~/test/lv4/static$ gcc -c name.c -o name.o
linux@linux:~/test/lv4/static$ ls
hello.c hello.o name.c name.o
linux@linux:~/test/lv4/static$ ar crs libstatic.a hello.o name.o
linux@linux:~/test/lv4/static$ ls
hello.c hello.o libstatic.a name.c name.o
linux@linux:~/test/lv4/static$ vi test.c
linux@linux:~/test/lv4/static$ gcc test.c -o test -L. -lstatic
linux@linux:~/test/lv4/static$ ./test
hello
My name is: Zdd
linux@linux:~/test/lv4/static$ cat -s hello.c
#include <stdio.h>
void hello()
{
printf("hello\n");
return;
}
linux@linux:~/test/lv4/static$ cat -s name.c
#include <stdio.h>
void name()
{
printf("My name is: Zdd\n");
return;
}
linux@linux:~/test/lv4/static$ cat -s test.c
#include <stdio.h>
void hello();
void name();
int main()
{
hello();
name();
return 0;
}
共享库
特点:程序编译(链接)时仅记录用到了哪个共享库中的哪个符号,不复制共享库中的代码,程序运行时需要加载库
优点:
- 程序不包含库中代码,尺寸小
- 多个程序可以共享一个库
- 库升级方便,不需要重新编译用到该共享库的程序
- 使用更加方便
共享库的创建及使用
- 确定库中的函数的功能,接口
- 编写库源码 hello.c bye.c
- 编译生成目标文件
gcc -c -fPIC hello.c bye.c -o hello bye
这里-fPIC 是指生成位置无关代码,即生成的目标文件代码可以被加载到任意位置执行,而不是在固定的地址才能执行(用到的是相对寻址,而不是绝对寻址),这是由共享库的特点决定的,必须要加此选项 - 创建共享库
gcc -shared -o libcommon.so.1 hello.o bye.o
共享库的名字必须 lib开头+库名+.so+.1(这通常是版本号)后面是添加的目标代码文件 - 为共享文件创建链接文件(目的: 是让编译器在编译时能够找到这个共享库)
ln -s libcommon.so.1 libcommon.so
生成的 链接文件必须由.so结尾,不能再有版本号什么的后缀 - 编写应用程序 test.c
- 编译应用程序 并链接共享库libcommon.so
gcc test.c -o test -L. -lcommon
- 加载共享库
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
由于对于共享库来说,它并不是直接将库中的代码复制过来,它在执行的时候需要加载这个库,而它的搜索路径默认是在缺省路径下,而这里我们创建的共享库就在当前文件夹下,因此需要添加共享库的加载路径(这是方式之一) - 执行程序
./test
就可以出结果
总之
编译函数生成目标文件 --> 创建共享库 -->链接共享库 -->编写程序,用到了共享库中的函数 --> 编译、链接共享库 -->加载共享库–> 执行
相比静态库的创建和使用,多了链接共享库 和 加载共享库 这两个步骤
示例如下:
linux@linux:~/test/lv4/common$ vi hello.c
linux@linux:~/test/lv4/common$ gcc -c -fPIC hello.c -o hello.o
linux@linux:~/test/lv4/common$ vi name.c
linux@linux:~/test/lv4/common$ gcc -c -fPIC name.c -o name.o
linux@linux:~/test/lv4/common$ ls
hello.c hello.o name.c name.o
linux@linux:~/test/lv4/common$ gcc -shared -o libcommon.so.1 hello.o name.o
linux@linux:~/test/lv4/common$ ls
hello.c hello.o libcommon.so.1 name.c name.o
linux@linux:~/test/lv4/common$ ln -s libcommon.so.1 libcommon.so
linux@linux:~/test/lv4/common$ ls
hello.c hello.o libcommon.so libcommon.so.1 name.c name.o
linux@linux:~/test/lv4/common$ vi test.c
linux@linux:~/test/lv4/common$ vi common.h (通常将共享库中的函数声明都放在一个单独的头文件中,方便使用)
linux@linux:~/test/lv4/common$ gcc test.c -o test -L. -lcommon
linux@linux:~/test/lv4/common$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
linux@linux:~/test/lv4/common$ ./test
hello!
my name is xdz!
linux@linux:~/test/lv4/common$ cat -s hello.c
#include <stdio.h>
void hello()
{
printf("hello!\n");
return;
}
linux@linux:~/test/lv4/common$ cat -s name.c
#include <stdio.h>
void name()
{
printf("my name is xdz!\n");
return;
}
linux@linux:~/test/lv4/common$ cat -s common.h
void hello();
void name();
linux@linux:~/test/lv4/common$ cat -s test.c
#include <stdio.h>
#include "common.h"
int main()
{
hello();
name();
return 0;
}
如何找到共享库
为什么需要加载共享库,上面已经解释过了。
为了让系统找到要加载的共享库,有三种方法:
- 把库拷贝到/usr/lib 和 /lib 目录下。(不推荐)
- 在LD_LIBRARY_PATH环境变量中添加库所在路径(缺点就是下次还需要再次添加)
- 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新