【Linux】静态库与动态库制作及运行原理

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法...感兴趣就关注我吧!你定不会失望。

本篇导航

  • [0. 静态库与动态库](#0. 静态库与动态库)
  • [1. 制作与使用静态库](#1. 制作与使用静态库)
  • [2. 制作与使用动态库](#2. 制作与使用动态库)
  • [3. 动态库是如何被加载到内存?](#3. 动态库是如何被加载到内存?)
  • 3.1程序地址空间

0. 静态库与动态库

先来总体描述下静态库与动态库的区别.

静态库是将头文件总体复制到可执行文件当中动态库是在可执行程序运行时进行了动态链接(所需要某个实现方法就去内存中查找).

所以静态链接所形成的可执行文件可以在没有相关配置的设备上运行,而动态链接的可执行程序对设备环境要求较高.

通常情况下,我们将自己的代码提供给别人使用时,往往只会提供: 头文件与源码打包成的库.这个库可以是动态链接也可以是静态链接.

  • 总结:动静态库都为将形成的二进制码进行链接的过程,二者区别仅为链接方式不同

1. 制作与使用静态库

接下来介绍如何制作一个自己的静态链接库.

我们先正常进行一段编码.分别编写头文件.h与方法.c

cpp 复制代码
// .h编写
#pragma once
int add(int x,int y);
cpp 复制代码
// .c编写
extern int myerrno;

int add(int x,int y)
{
    return x+y;
}

我们先使用如下指令对.c进行编译

bash 复制代码
gcc -c myadd.c
# 该指令会自动生成.c同名的.o文件

接下里就是对myadd.o进行链接的过程

我们使用ar工具带上rc选项来进行链接,具体指令如下

静态链接库的文件名格式为 libxxx.a

bash 复制代码
ar -rc libmyadd.a myadd.o

此时就编译完成了我们所需要的静态库.

现在我们试试写一段代码去引用下我们的方法.

cpp 复制代码
#include "myadd.h"
#include<stdio.h>
int main()
{
    printf("%d\n",add(1,2));
    return 0;
}

接下来我们试着用gcc进行编译运行看看

bash 复制代码
gcc -o test test.c

显示其不认识我们的add函数,但是我们不是将头文件包含了嘛?

这是因为gcc编译时会去特定的路径下查找头文件与库文件信息.

该目录结构一般如下

在lib/include路径下存放我们自定义的头文件 在lib/myaddlib存放我们打包生成好的库文件.

之后,手动指定gcc去哪里寻找对应的链接文件

bash 复制代码
gcc -o test test.c -I lib/include -L lib/myaddlib -lmyadd

其中 -I表示头文件在哪个路径下 -L表示链接文件在哪个路径下 -l表示具体是哪个链接文件的路径(此时要去掉前缀lib去掉后缀.a)

为什么头文件不需要指定名称了呢?因为我们在代码中已经手动include "头文件"了

那么为什么我们平常使用时没有这么麻烦呢?

因为系统在安装库时,把所有需要的文件放在了指定目录下,gcc编译时会自己去该目录进行寻找,我们仅需要制定形成的链接文件即可

头文件存储在 /usr/include中 形成的链接文件放在/lib64下

我们试试将我们的东西软连接到对应目录下

bash 复制代码
sudo ln -s $(pwd)/lib/include /usr/include/lib
bash 复制代码
sudo ln -s $(pwd)/lib/myaddlib/libmyadd.c /lib64/libmyadd.c

此时我们直接带上-l选项即可完成编译

bash 复制代码
gcc -o test test.c  -lmyadd

这也为安装静态库的方法,我们可以通过复制/软链接到对应的目录

在编译完成后该文件即可独立运行,此时将编译时所依赖文件全部删除也不影响该程序运行

makefile:

makefile 复制代码
lib=libmyadd.a

$(lib):myadd.o
	ar -rc $@ $^
myadd.o:myadd.c
	gcc -c $^  

.PHONY:clean
clean:
	rm -rf *.o lib *.a test *.a*
	
.PHONY:output
output:
	mkdir -p ./lib/include
	mkdir -p ./lib/myaddlib/
	cp *h ./lib/include
	cp *a ./lib/myaddlib

2. 制作与使用动态库

先来介绍一个指令: ldd + 可执行文件

可以列出当前文件运行时所需的动态库目录,例如我们刚刚使用的test

为了方便介绍,我们将多个方法.c文件打包成一个动态库文件

cpp 复制代码
// myPrintf.h
#pragma once
#include <stdio.h>
void Printf();
cpp 复制代码
// myLog.h
#pragma once
#include <stdio.h>
void Log();
cpp 复制代码
// myPrintf.c
void Printf()
{
    printf("hello myprintf\n");
}
cpp 复制代码
// myLog.c
void Log()
{
    printf("hello mylog\n");
}

将其进行编译成二进制文件

bash 复制代码
gcc -fpic -c myPrintf.c myLog.c

fpic为与位置无关码

将其进行链接

bash 复制代码
gcc -shared -o libmyfunc.so myPrintf.o myLog.o 

动态库的后缀为so,前缀为lib

此时一个名为 libmyfunc.so的可执行文件就被编译生成了.(至于为什么是可执行的我们之后再说)

为了规范 我们将其放进如下目录

同样编写一段代码尝试使用

cpp 复制代码
// test.c
#include "./lib/include/myLog.h"
#include "./lib/include/myPrintf.h"
int main()
{
    Printf();
    Log();
    return 0;
}

编译指令与静态库相似

bash 复制代码
 gcc -o mytest test.c -I ./lib/include -L ./lib/lib -lmyfunc

但是,此时我们直接运行会发现.

原因:动态库并不会将自己的库文件放进可执行程序,其运行时动态在内存中进行查找.而我们上面仅告诉了编译器我们的动态库在哪,而加载器并不知道,所以找不到

  • 方法也是将自己的动态库文件拷贝/软连接到/lib下
  • 一个方法是修改/etc/ld.so.conf.d下的文件内容

在该目录下创建一个.conf结尾的文件 ,将动态库所在的文件目录路径填入即可

bash 复制代码
sudo ldconfig
  • 还有一个方法是导入环境遍历 LD_LIBRARY_PATH后面带上上文的路径即可

3. 动态库是如何被加载到内存?

3.1程序地址空间

在文件被编译编程成可执行程序时,会有一套自己的内部地址.与PCB私有的栈帧模型类似

将代码放在了代码区,只读变量放在了只读变量区等...

他私有的地址,当其放入内存时,就有物理地址自动与其一一对应

当cpu访问对应代码时,会从内存中将其放入页表上,此时已经有物理地址与虚拟地址的映射.直接放入即可.

动态库与普通文件无异,也会被加载到内存中。当文件需要调用时,将其映射到PCB的共享区.

多个PCB可以映射同一个动态库文件.errno用到的技术为写时拷贝

函数内部调用会跳转到函数的虚拟地址.

若该函数为动态库的函数,则该函数存储的是从共享区开头到目标动态库函数的偏移量.

这就是动态库编译选项中与位置无关码FPIC的含义

当cpu访问对应代码时,会从内存中将其放入页表上,此时已经有物理地址与虚拟地址的映射.直接放入即可.

动态库与普通文件无异,也会被加载到内存中。当文件需要调用时,将其映射到PCB的共享区.

多个PCB可以映射同一个动态库文件.errno用到的技术为写时拷贝

函数内部调用会跳转到函数的虚拟地址.

若该函数为动态库的函数,则该函数存储的是从共享区开头到目标动态库函数的偏移量.

这就是动态库编译选项中与位置无关码FPIC的含义

相关推荐
练习时长一年3 小时前
jdk动态代理实现
java·开发语言
爬山算法3 小时前
Redis(83)Redis的缓存击穿是什么?
java·redis·缓存
Elieal3 小时前
IDEA使用教程
java·ide·intellij-idea
追风少年ii3 小时前
脚本更新--CosMx、Xenium的邻域通讯分析(R版本)
linux·python·r语言·r·单细胞·培训
执笔论英雄3 小时前
【大模型推理】ScheduleBatch 学习
java·spring boot·学习
馨谙3 小时前
Bash Shell 脚本编程入门详解
linux·bash
CRMEB系统商城4 小时前
【新版发布】标准版PHP v5.6.4正式版,优化部分用户体验
java·大数据·小程序·php·ux
青云交4 小时前
Java 大视界 -- Java 大数据在智慧养老服务需求分析与个性化服务匹配中的应用
java·需求分析·智慧养老·健康管理·java 大数据·个性化服务·生活照料
海星船长丶4 小时前
Chrony服务器同步时间服务器实验
运维·服务器
用户31187945592184 小时前
申威 SW-64 架构安装 MySQL 8.0.18 (KY10系统 RPM包) 步骤指南
linux