前言
该篇主要是描述如何制作静态库和动态库,以及静态库和动态库的使用,使用gcc编译,gcc编译的基础知识请参考我这篇博客Linux gcc编译基础知识
一、静态库
静态库的文件命名:libxxx.a,对应 Windows 的 *.ib 文件
1. 制作步骤
1. 编译为 *.o 文件
2. 将 *.o 文件打包:ar rcs libCalc.a file1.o file2.o file3.0 …
3. 将头文件和库一起发布
示例如下:
[lzy@localhost Calc]$ ls
include lib src
[lzy@localhost Calc]$ ls include src
include:
head.h
src:
add.c sub.c
[lzy@localhost Calc]$ cat include/head.h src/*.c
#ifndef __HEAD_H__
#define __HEAD_H__
int add(int, int);
int sub(int, int);
#endif
#include "head.h"
int add(int iNum1, int iNum2)
{
int iResult = iNum1 + iNum2;
return iResult;
}
#include "head.h"
int sub(int iNum1, int iNum2)
{
int iResult = iNum1 - iNum2;
return iResult;
}
[lzy@localhost Calc]$ cd src
[lzy@localhost src]$ gcc -c *.c -I ../include
[lzy@localhost src]$ ls
add.c add.o sub.c sub.o
[lzy@localhost src]$ ar -src libCalc.a *.o
[lzy@localhost src]$ ls
add.c add.o libCalc.a sub.c sub.o
[lzy@localhost src]$ nm libCalc.a
add.o:
0000000000000000 T add
sub.o:
0000000000000000 T sub
[lzy@localhost src]$ mv libCalc.a ../lib
[lzy@localhost src]$ ls ../lib
libCalc.a
[lzy@localhost src]$ ls ..
include lib src
至此,静态库打包发布已经完成,将 include 和 lib 文件夹提供给用户即可,部分参数解释如下:
ar:打包归档
-src:打包时的固定参数,不会变
libCalc.a:打包输出静态库文件名
*.o:指定要打包的 .o 文件
2. 使用静态库
废话不多说,直接上示例:
[lzy@localhost Calc]$ ls
include lib main.c src
[lzy@localhost Calc]$ cat main.c
#include <stdio.h>
#include "head.h"
int main(void)
{
int iSum = add(6, 3);
int iSub = sub(6, 3);
printf("Sum = %d\n", iSum);
printf("Sub = %d\n", iSub);
return 0;
}
[lzy@localhost Calc]$ gcc main.c -o app -I ./include -L ./lib -lCalc
[lzy@localhost Calc]$ ./app
Sum = 9
Sub = 3
补充说明:编译的时候,要指定头文件路径和库路径以及库名称,不然编译会报错
二、动态库
1. 制作步骤
1. 编译与位置无关的代码,生成 *.o ,关键参数 -fPIC(PIC:代码的位置无关性,可大写可小写)
2. 将 *.o 文件打包,关键参数 -shared
3. 将头文件和库一起发布
示例如下:
[lzy@localhost Calc]$ ls
include lib main.c src
[lzy@localhost Calc]$ cd src/
[lzy@localhost src]$ ls
add.c sub.c
[lzy@localhost src]$ gcc -fPIC -c *c -I ../include
[lzy@localhost src]$ ls
add.c add.o sub.c sub.o
[lzy@localhost src]$ gcc -shared -o libCalc.so *.o
[lzy@localhost src]$ ls
add.c add.o libCalc.so sub.c sub.o
[lzy@localhost src]$ mv libCalc.so ../lib
[lzy@localhost lib]$ ls ../lib
libCalc.so
[lzy@localhost lib]$ cd ..
[lzy@localhost Calc]$ ls
include lib main.c src
至此,动态库打包发布已经完成,将 include 和 lib 文件夹提供给用户即可,部分参数解释如下:
-fPIC:代码与位置无关
**-shared **:关键字,生成共享库
libCalc.so:打包输出动态库文件名
*.o:指定要打包的 .o 文件
2. 代码的位置无关性(PIC)
先看内存分布图:
补充说明:如上图,静态库是保存在代码段,作为程序的一部分,函数的地址固定;而动态库作为临时加载,函数地址不固定,故编译时地址不能固定,需要编译成和位置无关的代码;对于同一个动态库,函数相对地址偏移是固定的,在加载动态库调用函数时,通过动态库所在位置加上调用函数的相对地址偏移,找到该函数的地址,然后执行该函数,所以和位置无关。
3. 使用动态库
废话不多说,直接上示例:
[lzy@localhost Calc]$ gcc main.c -o app -I ./include -L ./lib -lCalc
[lzy@localhost Calc]$ ls
app include lib main.c src
[lzy@localhost Calc]$ ./app
./app: error while loading shared libraries: libCalc.so: cannot open shared object file: No such file or directory
[lzy@localhost Calc]$ ldd app
linux-vdso.so.1 => (0x00007ffd1436f000)
libCalc.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f870efd4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f870f3ab000)
哈哈,直接运行可执行环境是找不到库滴,程序只会查找系统路径或指定路径,编译时指定了所以编译过了,可以通过以下几种方法解决:
1. 将动态库 libCalc.so 拷贝到系统路径下面, /lib 或 /usr/lib(不推荐)
2. 将库路径增加到环境变量 LD_LIBRARY_PATH 中(不推荐),展示效果如下:
(1)一次生效
[lzy@localhost Calc]$ ls
app include lib main.c src
[lzy@localhost Calc]$ echo $LD_LIBRARY_PATH
[lzy@localhost Calc]$ export LD_LIBRARY_PATH=~/Project/Calc/lib/:$LD_LIBRARY_PATH
[lzy@localhost Calc]$ ldd app
linux-vdso.so.1 => (0x00007ffcdf557000)
libCalc.so => /home/atmuser/Project/Calc/lib/libCalc.so (0x00007fa12f935000)
libc.so.6 => /lib64/libc.so.6 (0x00007fa12f55f000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa12fb38000)
[lzy@localhost Calc]$ ./app
Sum = 9
Sub = 3
(2)永久生效
[lzy@localhost ~]$ ls -a
. .bash_profile .esd_auth .local 图片 桌面
.. .bashrc .ICEauthority makefile 公共 文档
.bash_history .cache include .mozilla 模板 下载
.bash_logout .config lib Project 视频 音乐
因为开机启动的时候回加载 .bashrc 文件,所以将 export LD_LIBRARY_PATH=~/Project/Calc/lib/:$LD_LIBRARY_PATH 写到到该文件即可永久生效
3. 修改 ld.so.conf 文件(推荐),因为系统启动时会加载该文件,可在其中配置一些链接库的路径,示例如下:
[lzy@localhost Calc]$ ls
app include lib main.c src
[lzy@localhost Calc]$ ldd app
linux-vdso.so.1 => (0x00007ffec4dfe000)
libCalc.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fe7c58cc000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe7c5ca3000)
[lzy@localhost Calc]$ sudo vim /etc/ld.so.conf
[sudo] password for lzy:
[lzy@localhost Calc]$ ldd app
linux-vdso.so.1 => (0x00007fff563e1000)
libCalc.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f6973121000)
/lib64/ld-linux-x86-64.so.2 (0x00007f69734f8000)
[lzy@localhost Calc]$ sudo ldconfig
[lzy@localhost Calc]$ ldd app
linux-vdso.so.1 => (0x00007ffdc2b35000)
libCalc.so => /home/atmuser/Project/Calc/lib/libCalc.so (0x00007f4431955000)
libc.so.6 => /lib64/libc.so.6 (0x00007f4431594000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4431b6c000)
[lzy@localhost Calc]$ ./app
Sum = 9
Sub = 3
[atmuser@localhost Calc]$
补充说明:sudo 打开 /etc/ld.so.conf 文件后,在文件最下面添加一行:*.so 库的绝对路径,保存退出后,必须执行 sudo ldconfig 命令,使配置立即生效,所有命令需要管理员权限,使用sudo运行
优缺点总结
1. 静态库
优点:
1. 执行快(直接加载源码,不需要查找函数地址)
2. 发布应用是不需要发布库
缺点:
1. 执行程序体积比较大
3. 库变更时需要重新编译应用
2. 动态库
优点:
1. 执行程序体积小
2. 库变更是,一般不需要重新编译应用(如果接口修改了,需要重新编译)
缺点:
1. 执行时需要加载动态库,相对而言,比静态库慢
2. 发布应用是需要同时发布动态库