文章目录
- 动态库和静态库
- 1. 静态库
- 2. 动态库
承接上文:
文件描述符
动态库和静态库
静态库与动态库:
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
1. 静态库
如果把我们提供的方法打包给别人用:
- 我们把源文件直接给别人
- 把我们的源文件想办法打包成库 = 库 + *.h
自己设计一个静态库:
mymath.h :
#pragma once
#include <stdio.h>
extern int myerrno;
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
mymath.c :
#include "mymath.h"
int myerrno = 0;
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
if(y == 0)
{
myerrno = 1;
return -1;
}
return x / y;
}
makefile:
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
.PHONY:clean
clean:
rm -f *.o *.a
注:ar 是 gnu 归档工具,rc 表示 (replace and create)
生成的库其实也就是把所编译的 .o 文件打包为 .a 的库文件!!
如果要发布库文件,这时来修改一下makefile文件代码:
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
.PHONY:clean
clean:
rm -rf *.o *.a output
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
发布:
创建lib文件成功:
现在来使用一下这个打包好的库:
新建一个目录(当作是用户目录),拷贝一下lib目录到该目录:
那么该怎么使用这个库呢?
新建main.c文件:
//#include "lib/include/mymath.h" //也可以这样用
#include "mymath.h"
int main()
{
printf("1 + 1 = %d\n", add(1, 1));
return 0;
}
写完c文件需要编译,但是如果我们直接编译是一定会报错的:
- 系统找不到头文件!!!
这是就需要在指定目录中编译了:
这里以我的为例: gcc main.c -I ./lib/include/
- 虽然编译依然还有报错,但已经不是头文件报错了。
报错提示找不到add函数,可以确定这里是链接式报错,主要是找不到add的实现方法。问题是还是找不到库的原因!!!
这是还需要再加一个指令条件: gcc main.c -I ./lib/include/ -L ./lib/mymathlib/
-L代表lib的首字母,表示让系统到 ./lib/mymathlib/ 这个目录中去查找add :
结果还是报错!!!!呜呜呜!!!
- 原因还是因为: 不知道查找指定目录中的哪一个库,还需要指定库的名称:
指令: gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -l libmymath.a
其中 -l 表示 link 的意思
结果还是出错,这里需要注意一点,指定库名称时:
- 要去掉前缀lib,要去掉后缀 .a,并且-l后要紧跟库名称,最好不要有空格
最终指令: gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath
最终,编译通过!!!
之所以使用 gcc 或 g++ 编译时,不需要加这些后缀,是因为其把lib库文件都安装到了系统目录中,可以直接识别到。
结论:
- 第三方库往后使用的时候,必定是要用gcc -l
- 深刻理解errno的本质
- 如果系统中只提供静态链接,则gcc只能对该库进行静态链接
- 如果系统中需要连接多个库,则gcc可以链接多个库
2. 动态库
先创建四个文件进行编写功能:
代码编写:
mylog.c
#include "mylog.h"
void Log(const char* info)
{
printf("Warrning: %s\n", info);
}
mylog.h
#pragma once
#include <stdio.h>
void Log(const char *);
myprintf.h
#pragma once
#include <stdio.h>
void Print();
myprint.c
#include "myprint.h"
void Print()
{
printf("hello new world\n");
printf("hello solity\n");
printf("hello pzh\n");
}
生成动态库编译选项:
- shared: 表示生成共享库格式
- fPIC :产生位置无关码(position independent code)
- 库名规则:libxxx.so
生成动态库:
-
编译 .o 文件指令:
-
gcc -fPIC -c myprint.c
gcc -fPIC -c mylog.c
- 生成.so 动态库 :
gcc -shared -o libmymethod.so *.o
以so为后缀的文件就是生成的动态库了!!
打包静、动库文件:
makefile:
dy-lib=libmymethod.so
static-lib=libmymath.a
.PHONY:all
all:$(dy-lib) $(static-lib)
$(static-lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
$(dy-lib):mylog.o myprint.o
gcc -shared -o $@ $^
mylog.o:mylog.c
gcc -fPIC -c $^
myprint.o:myprint.c
gcc -fPIC -c $^
.PHONY:clean
clean:
rm -rf *.o *.a *.so mylib
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
生成打包库文件:
使用库,这里专门进入一个空目录来调用动静态库查看效果:
先来使用一下静态库:
main.c代码:
#include "mymath.h"
int main()
{
printf("1 + 1 = %d\n", add(1, 1));
int n = div(10, 0);
printf("10 / 0 = %d, myerrno = %d\n", div(10, 0), myerrno);
return 0;
}
指令: gcc main.c -I mylib/include/ -L mylib/lib -lmymath
更改main函数,调用动态库:
#include "mylog.h"
#include "myprint.h"
int main()
{
Print();
Log("hello log function");
return 0;
}
跟静态库调用基本一样的指令: gcc main.c -I mylib/include/ -L mylib/lib -lmymethod
如果需要多种库就需要多种指明,如果需要同时调用动静态库,就需要如下指令:
gcc main.c -I mylib/include/ -L mylib/lib -lmymethod -lmymath
上面完全没问题,但是生成的可执行文件却执行不了:
那么此时应该如何加载动态库呢?
指令: sudo ln -s /home/solity/pzh/temp/test/mylib/lib/libmymethod.so /lib64/libmymethod.so
这里使用的是绝对路径,可以参考我当前的路径对照,进行修改为自己的路径:
此时,便大功告成了:
测试完成后,不要忘记删除刚刚创建的临时软链接库: sudo unlink /lib64/libmymethod.so
解决加载找不到动态库的方法:
-
拷贝到系统默认的库路径:
/lib/user/lib64
(强烈推荐,性价比高) -
在系统默认的库路径
/lib/user/lib64
下建立软连接 -
将自己库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中(默认是没有的,如果有可能是因为其他原因系统默认添加了)
然后将路径添加:
先来查看:
我这里是因为配置过vim自动添加的,默认是没有的!!
添加指令:
export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:/home/solity/pzh/temp/test/mylib/lib
后面的路径自己系统文件下的绝对路径:
对比上图,这里的动态库路径已经有了!!
这里就可以直接
./a.out
就可以直接运行了。 -
ldconfig 配置: /etc/ld.so.conf.d 建立自己的动态库路径配置文件,然后重新ldconfig更新即可
先进入该目录:
cd /etc/ld.so.conf.d
(注意:这里使用root用户进行操作)这些配置文件里面放的都是路径。
然后我们来新建一个conf文件:
将动态库的路径放到里面,保存退出!!
然后再进行使用指令 :
ldconfig
加载一下
此时就完成了:
这是来清空一下配置文件,重新加载:
可以看到刚才还有的路径,再次查看就已经没有了!!!
注意: 如果添加了配置文件,将不会随着关机重启而还原,配置文件一旦添加将永久拥有,除非自己删除!!
实际情况,我们用的库都是别人的成熟的库,都采用直接安装到系统的方式!!!
动态库再进程运行的时候,是要被加载的(静态库没有)。常见的动态库被所有的可执行程序(动态链接的)都要使用,动态库 ——— 共享库
所以 动态库在系统中加载之后,会被所有进程共享