【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的含义

相关推荐
盖世英雄酱58136几秒前
Java 内存管理技巧(新手必看集合篇)
java
码农小灰3 分钟前
Java 8 Stream API 入门到实践详解
java·java案例
家庭云计算专家5 分钟前
飞牛云一键设置动态域名+ipv6内网直通访问内网的ssh服务-家庭云计算专家
运维·云计算·ssh·nextcloud·ddns·动态域名解析
步、步、为营8 分钟前
.NET 事件模式举例介绍
java·开发语言·.net
cui_hao_nan11 分钟前
设计模式——模板方法
java·设计模式
小吕学编程11 分钟前
HttpServletRequest常用方法
java·http
在未来等你12 分钟前
Java并发编程实战 Day 11:并发设计模式
java·设计模式·多线程·并发编程·threadlocal·生产者消费者·读写锁
藥瓿锻18 分钟前
2024 CKA题库+详尽解析| 15、备份还原Etcd
linux·运维·数据库·docker·容器·kubernetes·cka
李少兄24 分钟前
解决 idea提示`SQL dialect is not configured` 问题
java·sql·intellij-idea
bbsh209927 分钟前
WebFuture:Ubuntu 系统上在线安装.NET Core 8 的步骤
linux·ubuntu·.netcore·webfuture