一、静态库
静态库是程序在编译链接时将库的代码链接到可执行文件中,生成的可执行文件不需要库也可以运行。静态库是以 .a 为结尾的。
打包静态库的方法:
1、假设我们有一系列的 .c 和 .h 文件,第一步就是把 .c 文件编译成 .o 文件,通过指令: gcc -c 【.c文件名】,就会生成同名 .o 文件,如图:
2、通过命令:ar -rc libXXX.a 【你要打包的 .o 文件名】,就可以生成静态库。如图: 其中 -r 代表replace,如果 .o 文件变化了,就用现在的替换原来的。-c 代表create,不存在 libXXX.a 就创建。在【libXXX.a】中,lib 表示前缀,.a表示后缀(静态库后缀),XXX是库的名字。
3、使用:因为gcc默认是在系统的默认路径下查找库的,因为我们是自己写的库,所以我们用gcc编译 main.c 时,要指明库的路径(-L)、库的名字(-l) [ 小写的L ] 、头文件的路径(-I) [ 大写i ] 具体指令:gcc -o 【可执行文件名】 -L【库的路径】-l【库的名字】-I【头文件路径】。注意:库的名字是去掉前缀和后缀的。例如:libmyc.a,名字是myc。
下图将静态库以及头文件放在了不同的目录下。
成功形成了可执行程序并运行!
二、动态库
动态库是程序在运行时才会去链接,多个程序可以共享同一个动态库。动态库是以 .so 为结尾的。
a、打包动态库的方法:
1、同样假设我们有一系列 .h 和 .o 文件,第一步就是把 .c 文件编译成为 .o 文件,与生成静态库时不同的是我们要加上选项 -fPIC ,这表示产生位置无关码。 指令:gcc -c -fPIC 【.c文件名】
2、将这些 .o 文件打包,打包方式也和生成静态库时不同,动态库的打包是直接使用gcc加上 -shared 选项。指令:gcc -shared libXXX.so 【.o文件名】。
3、使用库,与静态库的使用方法一致 ,因为gcc默认是在系统的默认路径下查找库的,因为我们是自己写的库,所以我们用gcc编译 main.c 时, a. 要指明库的路径(-L)、库的名字(-l) [ 小写的L ] 、头文件的路径(-I) [ 大写i ] b. 具体指令:gcc -o 【可执行文件名】 -L【库的路径】-l【库的名字】-I【头文件路径】。注意:库的名字是去掉前缀和后缀的。例如:libmyc.a,名字是myc。
b、使用动态库无法运行问题
我们从上图可以看到,我们成功的用gcc生成了可执行文件,但是无法运行,这是因为我们的动态库不在系统路径下,也不在当前目录下,所以系统无法找到我们的动态库。
那为什么静态库运行时不用考虑这些呢?因为静态库是在编译时把库链接进了可执行文件中,因此,运行时不需要再找了。
而我们用动态库时,编译器找到了库,形成了可执行文件,但系统没找到库,所以无法运行
解决方法:
1、把库放在当前路径下
2、把库拷进系统路径下(不推荐,因为我们写的库不成熟,不适合拷贝进系统默认路径)
3、在系统路径下建立我们写的库的软链接 (推荐)
4、把路径放进配置文件,在我们的 /etc/ld.so.conf.d 目录下,建立一个以 .conf 为结尾的文件,里面把路径写上,在用 sudo ldconfig 命令使配置文件生效。
在里面填上路径
总结:官方库推荐用第2种方法,其他推荐用第三种方法。
三、动静态库的加载
静态库的加载就是在编译时把库链接到可执行文件里面。
动态库的加载是把动态库加载到内存,然后不同的进程通过页表的映射可以访问库。
我们的程序在被加载到内存之前就已经根据不同的类别划分好了地址,静态库就是以绝对编址的方式加载到可执行程序里面。
进程创建时先创建task_struct、进程地址空间和页表,然后再加载到内存中,可执行文件里面编好的地址就是虚拟地址。
而动态库就是有一个起始地址在地址空间中,每一条语句都记录了偏移量,因此,不管动态库映射到哪,我们都可以通过起始地址+偏移量的方式找到对应的方法。