文章目录
- 生成动静态库
- 使用动静态库
对之前内容的复习:👉 [Linux](5)gcc编译、gdb调试、make/makefile项目自动化构建工具、进度条小程序_世真的博客-CSDN博客
- 静态库(.a)︰程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so)︰程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamiclinking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
生成动静态库
有这样四个文件:
mymath.h 头文件什么一个累加函数
// mymath.h
#pragma once#include <stdio.h>
#include <assert.h>// 累加
extern int addToVal(int from, int to);
累加函数的实现
// mymath.c
#include "mymath.h"int addToVal(int from, int to)
{assert(from <= to);int result = 0;for (int i = from; i <= to; ++i){result += i;}return result;
}
myprint.h 头文件声明一个打印函数
// myprint.h
#pragma once #include <stdio.h>
#include <time.h>extern void Print(const char* msg);
打印函数的实现,打印字符串和当前时间
#include "myprint.h"void Print(const char* msg)
{printf("%s : %lld\n", msg, (long long)time(NULL));
}
makefile:
mymath.o:mymath.cgcc -c mymath.c -o mymath.o
myprint.o:myprint.cgcc -c myprint.c -o myprint.o
.PHONY:clean
clean:rm -f *.o
将它们都处理成可重定位二进制文件(.o)
使用 ar -rc 命令将 .o 文件打包成静态库
ar -rc libmymath.a mymath.o myprint.o
生成动静态库的makfefile:
.PHONY:all
all:libmymath.so libmymath.alibmymath.so:mymath.o myprint.ogcc -shared -o libmymath.so mymath.o myprint.o
mymath.o:mymath.cgcc -fPIC -c mymath.c -o mymath.o
myprint.o:myprint.cgcc -fPIC -c myprint.c -o myprint.olibmymath.a:mymath_s.o myprint_s.oar -rc libmymath.a mymath_s.o myprint_s.o
mymath_s.o:mymath.cgcc -c mymath.c -o mymath_s.o -std=c99
myprint_s.o:myprint.cgcc -c myprint.c -o myprint_s.o -std=c99.PHONY:lib
lib:mkdir -p lib-static/libmkdir -p lib-static/includecp *.a lib-static/libcp *.h lib-static/includemkdir -p lib-dyl/lib mkdir -p lib-dyl/includecp *.so lib-dyl/lib cp *.h lib-dyl/include.PHONY:clean
clean:rm -rf *.o *.a *.so lib-static lib-dyl
使用动静态库
静态库
- 通过相对路径包含头文件
#include "./lib-static/include/mymath.h"
#include "./lib-static/include/myprint.h"int main()
{int start = 0;int end = 100;int result = addToVal(start, end);printf("result: %d\n", result);Print("hello world");return 0;
}
- 将头文件和我们编写的程序放在同一路径下,包含头文件就可以不用带路径了
- 将头文件放在系统头文件路径下
/user/include
,包含头文件也可以不用带路径了
光是这样写还不行,它会报链接错误
[CegghnnoR@VM-4-13-centos test]$ gcc mytest.c -o mytest
/tmp/cc8M56hj.o: In function `main':
mytest.c:(.text+0x21): undefined reference to `addToVal'
mytest.c:(.text+0x42): undefined reference to `Print'
collect2: error: ld returned 1 exit status
这是因为找不到库文件,所以库文件也应该拷贝到系统路径下。
将自己的头文件和库文件,拷贝到系统路径下的过程就叫做库的安装。
不过这种方式不推荐,容易污染系统头文件和库。
我们还有一种方式去只用自己的库
gcc mytest.c -o mytest -I ./lib-static/include/ -L ./lib-static/lib/ -l mymath
使用 gcc
-I
选项指定头文件所在目录,使用 -L
选项指定库文件所在目录,使用 -l
选项指定要包含的库名(去掉前缀lib和后缀.a)
动态库
和静态库一样去指定头文件目录、库文件目录、库名
gcc mytest.c -o mytest -I ./lib-dyl/include/ -L ./lib-dyl/lib/ -l mymath
编译成功,但是运行会出问题:
[CegghnnoR@VM-4-13-centos test]$ ./mytest
./mytest: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
使用 ldd 查看程序依赖的库:
[CegghnnoR@VM-4-13-centos test]$ ldd mytestlinux-vdso.so.1 => (0x00007ffdc9def000)/$LIB/libonion.so => /lib64/libonion.so (0x00007f2c913c4000)libmymath.so => not foundlibc.so.6 => /lib64/libc.so.6 (0x00007f2c90edd000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f2c90cd9000)/lib64/ld-linux-x86-64.so.2 (0x00007f2c912ab000)
运行时链接不到库,因为我们只告诉了 gcc 各种路径。而动态库是程序运行时去链接的,我们的进程找不到对应的动态库。
解决方法:
-
将动态库拷贝到系统路径下/lib64
-
导入环境变量 —— 程序运行时会在环境变量中查找自己需要的动态库路径
LD_LIBRARY_PATH
将自己的库文件的绝对路径添加到这个环境变量中,注意用
:
分隔 -
系统配置文件
-
软链接
程序运行需要把代码和数据加载到内存,那么动态库被加载到内存的哪个区了呢?
动态库被加载到物理内存后,会被映射到虚拟进程地址空间的共享区。如果有其他进程也要使用这个动态库,系统不会重新加载这个动态库,而是直接将它映射到该进程的共享区。所以一个动态库在内存中只会加载一份,是可以被多个进程共享的,所以动态库又叫共享库。