一、对库的理解
理解库
什么是库?比如C语言中的math库,python中的numpy库,库其实是预先编译好的、二进制代码模块集合,其实就是把通用功能的代码提前写好、编译成特定格式的文件,使用者不用重复编写这些通用逻辑,直接调用即可。核心还是方便人们使用!
制作库的理解:一个程序经过预处理、编译、汇编形成.o(Linux下) / .obj文件(Windows下),然后把这些.o文件链接形成可执行,那库和这个有什么关系呢? 我想用你写的代码,可以把.o文件(也需要头文件)拿过来链接就可以使用了,但是一个正常的项目库,比如numpy库这种,.o文件非常的多,所以对.o文件进行打包处理形成了库,动静态库的处理方法也一定不相同! 所以,我们用的库大多数都是.o的集合,提供功能和方法方便开发者使用!
理解动静态库
有了库的理解之后我们再来理解一下动静态库,在动静态链接中我们已经提到一次了,这里简单复习和扩展一下。
静态库:程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库:程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
随便写一份代码来看看动静态库的链接。

ldd---查看一个可执行所依赖的库文件。
依赖的就是libc.so.6, 这是个软链接。C标准库,在/lib64/下存在着大量的动态库,这也是系统默认的查找路径,后面还会提。
Linux中.so为后缀的文件是动态库,.a文件为后缀的是静态库。
Windows中,.dll为后缀的是动态库,.lib为后缀的是静态库。
file + 文件名可以查看文件信息

显示出来的符合预期,dynamically linked---动态链接。
g++ / gcc默认是动态链接的,带-static选项可以让它强制静态链接。

statically linked----静态链接。同时ldd也显示不是动态可执行。
动静态库优缺点

静态库:程序在编译链接的时候把库的代码链接到可执行文件中。
优点:使用静态库生成可执行程序可以直接运行,不再需要动态库,对于自己写的库不需要手动链接。
缺点:使用静态库生成的可执行程序空间会大很多,由上图可以看出差距之大。而且这只是包含了一个头文件,当有多个静态程序同时加载相同的库,内存还会存在大量的重复代码。所以系统默认不用静态库。
动态库:动态库是在程序运行阶段才引入代码的,程序地址空间中我们提到过堆栈相对而生,中间是共享区,动态库就被加载到了这,运行可执行的时候,可执行先被加载到内存中,当要使用动态库的函数的时候,先加载到物理内存中,通过页表映射到虚拟内存的共享区中,这样就可以使用了。
优点:能节省磁盘和内存空间,并且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存中不会存在重复代码
缺点: 运行时依赖动态库,并且自制动态库的使用相对复杂,因为需要手动链接。
二、制作静态库
比如模拟一个加减乘除
mymath.c
cpp
#include"mymath.h"
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 myerrno = 0;
int divi(int x,int y)
{
if(y == 0)
{
myerrno = 1;
return -1;
}
return x / y;
}
mymath.h
cpp
#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 divi(int x,int y);
main.c
cpp
#include<stdio.h>
#include"mymath.h"
int main()
{
int x = divi(4,2);
printf("x:%d\n",x);
return 0;
}
过程
怎么生成静态库呢?首先肯定要生成.o文件,然后使用ar -rc打包
ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面是一些常用选项:一般就使用ar -rc
-r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的
-c(create):建立静态库文件
t:列出静态库中的文件
v:详细信息

用makefile生成就行,写法:
makefile
libmymath.a:mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^ -o $@
clean:
rm -rf main
写完直接gcc main.c发现失败了,报错原因是divi未定义,这个其实可以理解,系统又不知道要主动链接你,所以需要g++需要 -l 选项,表示指定我链接的是这个库,另外,库名字是去掉前缀lib和后缀的,也就是说应该是gcc main.c -o main -lmymath, 在线程那里我们还会用到。链接多个库的话就带多个-l即可。
一输入发现又失败了

这里显示cannot find -lmymath也就是说,系统默认路径下找你这个库文件没有找到,当然找不到了!你只让我找库名为这个的,你又没告诉我在哪找!-L +路径,表示在指定库路径中寻找!
细节
这里有个非常恶心人的坑,就是如果你用的是cpp文件,这里可能会编译不通过,但是一切都没问题了啊,此时报错又变成了未定义,我查了查,发现:
用g++编译文件 main.cpp,但库文件 libmymath.a 是由 C 文件 mymath.c 编译生成的 ------C 和 C++ 的函数名修饰规则不同:
C 语言的 divi(int, int) 编译后,符号名是简单的divi;
C++ 会对函数名进行 "修饰",比如 divi(int, int) 会被编译为 _Z4divii 这类复杂符号。
所以,这里统一用.c即可。。。
编译成功后,删除.a文件,仍然可以运行
这里再提一个点,用系统的库的时候,是不需要加任何选项的,因为gcc的头文件默认搜索路径是:/usr/include 库文件的默认搜索路径是:/lib64 或者 /usr/lib64
那头文件是如何搜索的呢? 我们知道包含系统库的使用使用的是< >,自己的是" ",如果是< > 就会去/usr/include/下去找,找不到就报错,如果是" ",先去当前目录找,找不到再去/usr/include/下去找,找不到就会报错,当然,gcc也有选项-I, 表示指定搜索头文件路径。
那我们把自己的libmymath.a放到/lib64下就可以只gcc main.c 了吗?不可以!gcc不会主动链接你!只会主动链接核心系统库,比如libc.so,如果随便链接,会导致可执行文件非常大并且冲突。你需要告诉gcc链接哪个库,当然这样就不需要指定路径了,/usr/include/下找得到。并且格式必须是libxxx.a,gcc只会搜索正确格式的库!
当然,这里一整一大串不是很舒服,我们就可以采用一些方式:
1.可以将文件拷贝到系统路径下
sudo cp lib/include/mymath.h /usr/include/
sudo cp lib/XXXX/libmath.a /lib64/ or /usr/lib64/
不建议这么做。
2.建立软链接----软链接的作用体现出来了!可以跨目录!
使用绝对路径
sudo ln -s xxxx /usr/include/xxx
sudo ln -s xxx /lib64/libxxx.a
三、制作动态库
过程
生成.o的时候要带 -fPIC选项,表示产生位置无关码(position independent code),然后gcc带上shared选项生成动态库!
cpp
libmymath.so:mymath.o
gcc -shared $^ -o $@
mymath.o:mymath.c
gcc -fPIC $@ -c $^
-fPIC作用于编译阶段,其目的是告诉编译器产生与位置无关的代码,这时产生的代码中没有绝对地址,全都是相对地址.
生成完用法也和上面一样使用即可。
gcc main.c -o main -lmymath -L ./

另外:如果既存在动态库又存在静态库,不带static选项,系统会默认链接动态库,如果动态库不存在,系统只能默认链接静态库。
编译成功后,删除.so文件,发现无法运行,因为是动态库,需要动态去找!
找动态库的方式
我们知道,.so是运行时才会被加载到内存的,这就说明他一定能被找到!但是系统不会去找啊,那为什么上面提到的./main可以编译成功呢??
注意:gcc main.c -o main -lmymath -L ./这里是带路径了的!所以我们运行main的时候可以找得到!
有没有其他方式呢??包有的,首先就是上面提到的两种,一种拷贝到/usr/lib64/下,这种不推荐,另一种建立软链接,比如:sudo ln -s /home/caugyj/libmymath.so /usr/lib64/libmymath.so,一定要使用绝对路径
还有两种方式:
1.环境变量 LD_LIBRARY_PATH 修改这个环境变量加上自己的库的所在路径, xxx/mylib/lib

2 . /etc/ld.so.conf.d建立自己的动态库路径的配置文件,
(注意以.conf结尾,文件名无所谓,在root账号下使用或者sudo)
也是加上自己的库的所在路径,然后重新输入ldconfig指令即可.
这个永久有效,关掉xshell再打开可以使用

实际情况,我们用的库都是别人成熟的库,都采用直接安装到系统的方式!
动态库在进程运行的时候,是要被加载的(静态库没有),常见的动态库被所有的可执行程序(动态链接的),都要使用,动态库--共享库
所以,动态库在系统中加载之后,会被所有进程共享! -shared