动/静态库的原理及制作

一.基本概念

**动态库:**动态库就像一个仓库其中包含为可执行文件,如果有文件需要调用,就进行提取,使用动态库编译的文件因为保存的地址所以文件大小相对较小,依赖系统环境。

**静态库:**相当于2进制的.c文件,用来编译在文件内部,并且有相对较强的可移植性,若文件采用静态库进行编译,不依赖系统环境,但是文件体积相对较大。

二.动态库的制作

(1)将.c源文件编译为.o文件

制作动态库所需要的为有段落行的二进制文件编译时需要加特定的命令

gcc main.c -o main.o -c -fPIC

-c : GCC 会将指定的源代码文件(如 .c.cpp 文件)编译成目标文件(通常为 .o.obj 文件),但不会尝试将它们链接成可执行文件。这意味着编译器不会处理外部引用,只关注于源代码内部的编译。

**-fPIC:**GCC 会生成位置无关代码,这意味着代码中的所有地址都是相对于某个基址

(2)动态库可以当作共享的.so文件

gcc main.o -shared -o libmain.so

**-shared:**用于生成共享库(Shared Library,也称为动态链接库,以便可以被多个程序共享使用。

++其中有一些潜规则在进行连接"-l"的时候其中库的名称时掐头去尾 例如libmain.so其实时main++

(3)运行

<1>最简单直接的方法:将生成的.so文件移送到系统的lib文件夹下 编译命令如下

gcc main.c -o main -L <系统lib的路径一般时/lib> -l <库的名称>

<2>给链接库知名路径

gcc main.c -o main -L. -lmain -Wl,-rpath=/home/gec/lib

**-Wl :**在参数后的逗号分隔的参数会被直接传递给底层链接器

**-rpath:**用来给编译器指明所连接库的位置

<3>修改环境变量(只有在当前终端生效)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/gec/lib

<4>修改系统的环境变量

gec@ubuntu:~ sudo vi /etc/ld.so.conf.d/libc.conf gec@ubuntu:\~ sudo ldconfig

三.静态库的制作

假设功能文件 a.c、b.c 包含了一些通用的程序模块,可以被其他程序复用,那么可以将它们制作成静态库,具体的步骤是:

(1)制作 *.o 原材料

gec@ubuntu:~$ gcc a.c -o a.o -c

gec@ubuntu:~$ gcc b.c -o b.o -c

(2)将 *.o 合并成一个静态库

gec@ubuntu:~$ ar crs libab.a a.o b.o

(3)静态库的常见操作

ar -<参数> 静态库名称

-t:意即table,以列表方式列出*.o文件

-d:d意即delte,删除掉指定的*.o文件

-r:r意即replace,添加或替换(重名时)指定的*.o文件

-x:x意即extract,将库中所有的*.o文件释放出来

(4)使用

gcc main.c -L /path/to/libmain.a -l main -o main

其中路径一定为绝对路径

四.动态加载库

1.接口代码实现

复制代码
// a.c
void detection()
{
    printf("正在检测颜色是否均匀...\n");
}

// b.c
void detection()
{
    printf("正在检测外观是否破损...\n");
}

2.将不同的模块制作成动态库

gec@ubuntu:~$ gcc a.c -o a.o -c -fPIC

gec@ubuntu:~$ gcc -shared -fPIC -o libcolor.so a.o

gec@ubuntu:~$

gec@ubuntu:~$ gcc b.c -o b.o -c -fPIC

gec@ubuntu:~$ gcc -shared -fPIC -o libshape.so b.o

gec@ubuntu:~$

gec@ubuntu:~$ ls

libcolor.so libshape.so

3.编写一个配置文件,指定程序需要加载的动态库:

复制代码
gec@ubuntu:~$ cat config
libcolor.so

示例代码

cpp 复制代码
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>


int main(int argc, char **argv)
{
	// 读取配置文件
	FILE *fp = fopen("config", "r");
	char *lib = calloc(1, 30);
	fgets(lib, 30, fp);
	fclose(fp);

	// 根据配置文件打开指定的库
	void *handle = dlopen(strtok(lib, "\n"), RTLD_NOW);
	if(handle == NULL)
	{
		printf("加载动态库[%s]失败:%s\n", lib, strerror(errno));
		exit(0);
	}

	// 在库中查找事先约定好的接口
	void (*detect)(void);
	detect = dlsym(handle, "detection");
	if(detect == NULL)
	{
		printf("查找符号[%s]失败:%s\n", "detect", strerror(errno));
		exit(0);
	}

	// 潇洒地调用该接口
	detect();
}

五.函数解析

打开和关闭动态库,获取动态库的操作句柄:

关键点:

  • RTLD_LAZY意味着打开动态库时,并不立即解析库中的函数符号的内存位置,而是等待程序实际调用时才临时去解析。
  • RTLD_NOW与上述含义相反,它意味着打开动态库时就立即解析库中的函数符号的内存位置。
  • 不管是LAZY还是NOW,库中的静态数据符号都将被立即解析。

关键点:

  • 该函数用于在动态库中获取指定的函数入口地址。
相关推荐
求知若渴,虚心若愚。1 小时前
Error reading config file (/home/ansible.cfg): ‘ACTION_WARNINGS(default) = True
linux·前端·ansible
π大星星️2 小时前
Nginx 四层(stream)反向代理 + DNS 负载均衡
运维·nginx·负载均衡
beyoundout3 小时前
HAproxy
linux·运维·服务器
qq_218753315 小时前
服务器查日志太慢,试试grep组合拳
运维·服务器
Jie_176 小时前
【linux】高可用集群Keepalived
linux·运维·服务器
思绪漂移6 小时前
阿里云【免费试用】Elasticsearch 智能运维 AI 助手
运维·elasticsearch·阿里云
aiprtem6 小时前
LVGL + ESP-Brookesia 嵌入式模拟桌面应用开发
linux·c语言·物联网
21号 17 小时前
4.应用层自定义协议与序列化
运维·服务器·网络
xx.ii8 小时前
4.Linux 应用程序的安装和管理
linux·服务器·网络
奋斗的蛋黄9 小时前
解析分区、挂载与块设备:Linux 存储管理核心命令详解
linux·服务器·网络