目录
一、库的概念与类型
二、库的组成文件(.o)
2.1 初始源代码(.c .h)
2.2 可重定位二进制文件(.o)
2.3 库文件(lib) 与 头文件(.h)
三、实现静态库(.a)
3.1 建立静态库文件
3.2 交付库
3.3 编译器在编译链接中的作用
3.4 使用第三方静态库与头文件
3.4.2 只指定头文件路径(×)
3.4.3 指定头路径与库文件路径(×)
3.4.4 指定头文件路径和库文件名称(√)
3.5 C编译器默认动态链接
3.6 安装第三方静态库与头文件
四、实现简单的动态库
4.1 动态库建立打包
4.2 动态链接需要给OS提供库的路径
4.3 OS找到动态库方法
4.3.1 环境变量法(临时法)
4.3.2 配置文件法
4.3.3 建立软链接(或者直接添加到系统默认路径下)
五、动静态库加载理解
5.1 静态库加载理解
5.2 动态库加载理解
一、库的概念与类型
(百度)
库是程序代码的集合,是共享程序代码的一种方式。根据源代码的公开情况,库可以分为两种类型:
1. 开源库,公开源代码,能看到具体实现,比如:SDWebImage、AFNetworking。
2. 闭源库,不公开源代码,是经过编译后的二进制文件,看不到具体实现。闭源库主要分为:静态库和动态库。
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库!
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
二、库的组成文件(.o)
2.1 初始源代码(.c .h)
//add.h
#pragma
#include<stdio.h>
extern int add(int a ,int b);
//sub.h
#pragma
#include<stdio.h>
extern int sub(int a,int b);
//sub.c
#include"add.h"
int add(int a, int b)
{
return a + b;
}
//add.c
#include"sub.h"
int sub(int a ,int b)
{
return a - b;
}
//main.c
#include"add.h"
#include"sub.h"
int main()
{
int a=9,b=1;
printf("%d+%d=%d\n",a,b,add(a,b));
printf("%d-%d=%d\n",a,b,sub(a,b));
return 0;
}
2.2 可重定位二进制文件(.o)
这种文件将我们.c源代码变成了二进制文件,它依然可以链接形成exe文件,但是它们本身是二进制文件,所以没有暴漏了原来.c的源代码!我们可以将这些.o文件打包Output,用户拿着这些文件链接形成exe文件!
2.3 库文件(lib) 与 头文件(.h)
将我们.o文件集合打包成一个文件,这种文件我们称为库文件(lib)!
系统默认头文件路径 /usr/include/
默认库文件路径 /usr/lib64/交付库--》将.a/.so 文件以及其对应的.h文件交给用户!
三、实现静态库(.a)
3.1 建立静态库文件
//makefile
//静态库.a
libmymath.a:add.o sub.o
ar -rc $@ $^ //ar -rc 打包.o文件-》静态库(.a)
add.o:add.c
gcc -c $^ -o $@
sub.o:sub.c
gcc -c $^ -o $@
.PHONY:clean
clean:
rm -f *.o
3.2 交付库
3.2.1 发布库
libmymath.a:add.o sub.o
ar -rc $@ $^
add.o:add.c
gcc -c $^ -o $@
sub.o:sub.c
gcc -c $^ -o $@
//发布
.PHONY:output
output:
mkdir -p mylib/include //当前路径下建立头文件目录
mkdir -p mylib/lib //当前路径下建立库文件目录
cp *.h mylib/include //头文件打包
cp *.a mylib/lib //库文件打包
.PHONY:clean
clean:
rm -f *.o
3.2.2 用户下载库
3.3 编译器在编译链接中的作用
编译器会在编译链接中查找库与头文件!
系统库与头文件下:
编译器如何寻找头文件?
我们在写代码的时候包含头文件,编译器会拿着这些头文件名称到系统指定路径(/usr/include/)路径下寻找!
编译器如何寻找库文件?
同样,编译器会到指定路径去寻找库!用户没有直接提供库名称,但是编译器会知道找哪些库!
编译器默认链接方式:动态库链接!
3.4 使用第三方静态库与头文件
3.4.1 单纯使用gcc -o (×)
3.4.2 只指定头文件路径(×)
3.4.3 指定头路径与库文件路径(×)
3.4.4 指定头文件路径和库文件名称(√)
库的名称是去掉前缀(lib)和后缀的(.a/.so)剩下的!
3.5 C编译器默认动态链接
一个程序可以链接很多库文件,如果既可以链接动态库又可以链接动态库,则编译器默认选择链接动态库!除非只有一种库形式存在,否则链接方式由编译器决定!
3.6 安装第三方静态库与头文件
将第三方库文件与头文件拷贝至我们的对应的系统默认路径下:
系统默认头文件路径 /usr/include/
默认库文件路径 /usr/lib64/这样我们就可以不带路径找到头与库文件,但是我们必须还要指定链接的库文件名称!
四、实现简单的动态库
4.1 动态库建立打包
4.2 动态链接需要给OS提供库的路径
同样的操作,动态链接这样依旧不能运行!发现我们依旧打不开库文件!编译链接可以成功但是运行不行!编译链接时编译器的工作,我们把路径和库文件名告诉了编译器,所以可以编译链接成功!但是运行归OS管,OS找不到第三方库!OS只会在系统默认库路径下或者当前路径下查找!所以我们必须将我们的库文件安装到默认路径下!
4.3 OS找到动态库方法
4.3.1 环境变量法(临时法)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库文件路径!
4.3.2 配置文件法
cd /etc/ld.so.conf.d/ 系统目录,查看配置文件
创建.conf类型的目录并将动态库路径添加到这个目录下!
sudo ldconfig 更新缓存刷新一下
4.3.3 建立软链接(或者直接添加到系统默认路径下)
OS只会在系统默认库路径下或者当前路径下查找,所以我们可以:
1.在当前路径下建立库文件的软链接
2.在系统库路径下添加库文件的软链接
五、动静态库加载理解
5.1 静态库加载理解
静态链接是将静态库代码拷贝到进程的代码区,这些代码在代码区被编址!这样相当于这部分拷贝代码本身就属于我们这个进程!
5.2 动态库加载理解
动态库链接,当我们进程代码用到了外部库函数调用/变量,OS会将其对应的动态库加载到内存中,虚拟地址空间共享区与之映射!
外部库加载到内存后,进程如何准确找到外部库对应的函数?
进程中的外部库函数代码本身存载了自己在库中的地址偏移量!通过相对共享区起始位置的偏移量在共享区中找到自己的虚拟地址,然后映射到物理内存中找到我们函数的定义!