Linux_动静态库

概念:

什么是静态库?

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库

形象理解:就相当于把库代码重新拷贝到了这个可执行文件中。

简单来说:提供源代码的,提供二进制的,最后形成可执行时,把静态库中你要用的东西把它拷贝过去编译,后面死活跟我没关系,静态库不会加载到内存!

什么是动态库?

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

如何理解可执行权限?

你当前文件是否可以以可执行程序的方式加载到内存,这就叫可执行权限,库虽然没有main函数,但它有办法,所以它也有(库)可执行的特征:不是不能执行,只是不能单独执行,需要别人来用它才能执行!

形象两者区别:你玩游戏,动态库相当于如果你没有自己的电脑,就需要去网吧那里的电脑才能玩上游戏,此时的电脑资源是共享的,而静态库就相当于,你自己买了一部电脑,这时候你就不需要再去网吧,在自己家就可以玩上游戏了。属于你自己的了,不必再共享。

同样,我们也可以发现两者之间的优势与劣势:

维度   静态库(Static Library)    动态库(Dynamic Library)
文件体积 劣势:可执行文件体积大(包含库代码)。 优势:可执行文件体积小(仅含引用)。
运行依赖   优势:不依赖外部文件,可独立运行(移植性强)。 劣势:依赖系统中存在对应动态库,缺失会导致“运行报错”。
内存占用    劣势:多个程序使用时,各自加载一份,内存冗余。优势:多个程序共享一份库代码(系统全局缓存),节省内存。
更新维护   劣势:库更新后,所有依赖程序需重新编译链接。   优势:库更新后,仅需替换动态库文件,依赖程序无需重新编译。
编译速度   优势:编译时直接复制代码,链接速度快。   劣势:编译时需处理动态链接逻辑,速度略慢。
代码安全性    优势:库代码嵌入程序,不易被篡改或替换。    劣势:库文件独立存在,存在被恶意替换的风险。
 
库真实的名字:是去掉前缀,去掉后缀,剩下的叫库名字!

我们提供的方法给别人用一般有两种方式:

1.我们把源文件直接给他

2.我们的源代码想办法打包成库=库+.h

使用静态库:

创建

mymath.h

#pragma once

extern int myerrno;

int mydiv(int a,int b);

mymath.c

#include<stdio.h>
#include "mymath.h"
int myerrno=0;

int mydiv(int a,int b)
{
  if(b==0)
  {
    myerrno=1;
    return -1;
  }
  myerrno=0;
  return a/b;
}

test.c测试

#include<stdio.h>
#include "mymath.h"

int main()
{
  int a=10,b=0;
  int ret=mydiv(a,b);
  printf("%d/%d=%d,errno:%d",a,b,ret,myerrno);
 //printf("%d/%d=%d,errno:%d",a,b,myvid(a,b),myerrno);
 //注意:C语言中,形参的实例化时,是从右向左进行实参实例化的,因此,这样写的话,可能会导致结出
 //现错误~
 return 0;
}

二:编译生成静态库

将mymath.c编译为静态库libmymath.a

# 编译生成目标文件
gcc -c mymath.c -o mymath.o
# 归档生成静态库
ar rcs libmymath.a mymath.o

ar是gnu归档工具,专门用来打包静态库
rc表示(replace and create)

查看静态库中的目录列表

t:列出静态库中的文件

v:verbose 详细信息

安装静态库:

# 安装头文件到/usr/include
sudo cp mymath.h /usr/include/
# 安装静态库到/lib64
sudo cp libmymath.a /lib64/

使用

-L 指定库路径-l 指定库名

测试目标文件生成后,静态库删掉,程序照样可以运行。

-I(i的大写):指明我们的头文件在哪里

-L:库文件所在的路径在哪里

-l(L的小写):你要链接库的名称

注意:自己代码中,被头文件声明,源文件定义,在整个C库里只有一份,所以一旦出现错误时,你就可以看见了!

在第三方库,往后使用的时候,必定是要使用gcc -l 的

为什么之前C/C++不用?

因为它已经提前内置了

头文件默认有、库路径默认gcc能找到。

并且,你写的是纯C/C++代码,gcc/g++既然叫这个,就能认识C/C++的动态库和静态库。

动态库的链接:

编译生成动态库:

# 编译生成位置无关代码(动态库必需)
gcc -fPIC -c mymath.c -o mymath.o
# 链接生成动态库(-shared 指定动态库,-o 指定输出名)
gcc -shared mymath.o -o libmymath.so

动态库的使用:

找不到库

方法一:

显示指定动态库路径编译

我们之前写C/C++时,都是站在巨人的肩膀上

C语言怎么能找到C库呢?

因为加载的时候也要有对应单独路径,不仅仅编译需要

在我们之前学指令时,要带./,而系统不用,是因为系统中有环境变量path.

gcc,编译器是有自己的搜索路径的,加载也一样,因为系统在加载的时候会默认去自己的某些路径下去进行搜索对应的动态库,都是系统内置好的。

那么,当我们自己的库找不到动态库时,有以下方法解决:

解决加载找不到动态库的方法:

1.拷贝到系统默认的库路径/lib64 或者 /usr/lib64/

2.在系统默认的库路径/lib64 或者 /usr/lib64下建立软连接

3.将自己的库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中

4./etc/ld.so.conf.d 建立自己的动态库路径的配置文件,然后重新ldconfig

方法1:拷贝到系统默认库路径

# 假设动态库为libmymath.so,拷贝到/lib64
sudo cp libmymath.so /lib64/
# 或拷贝到/usr/lib64
sudo cp libmymath.so /usr/lib64/

# 假设动态库在当前目录,建立软连接到/lib64
sudo ln -s $(pwd)/libmymath.so /lib64/libmymath.so
# 或建立到/usr/lib64的软连接
sudo ln -s $(pwd)/libmymath.so /usr/lib64/libmymath.so

法二:方法2:在系统默认库路径建立软连接

# 假设动态库在当前目录,建立软连接到/lib64
sudo ln -s $(pwd)/libmymath.so /lib64/libmymath.so
# 或建立到/usr/lib64的软连接
sudo ln -s $(pwd)/libmymath.so /usr/lib64/libmymath.so

方法3:添加路径到LD_LIBRARY_PATH环境变量

# 临时添加(当前终端有效)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
# 永久添加(将命令写入~/.bashrc,再执行source ~/.bashrc)
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)" >> ~/.bashrc
source ~/.bashrc

方法4:通过/etc/ld.so.conf.d配置

# 新建配置文件(以mymath.conf为例)
sudo touch /etc/ld.so.conf.d/mymath.conf
# 将动态库路径写入配置文件
echo $(pwd) | sudo tee /etc/ld.so.conf.d/mymath.conf
# 刷新系统库缓存
sudo ldconfig

ps:由于我们通常用的是别人成熟的库,因此,我们一般都是采用第一种方法:安装拷贝到系统当中。

动态库在进程运行的时候,是要被加载的(静态库没有)

常见的动态库被所有的可执行程序(动态链接的),都要使用,因此,动态库也被称为共享库。

总结:动态库在系统中加载之后,会被所有进程共享!

接下来,我们就来谈谈它在Linux下是如何做到的?

但是,事实上,系统在运行中,一定会存在多个动态库,,因此,OS就必须要管理起来。

怎么管理,”先描述,再组织“,从而在系统中,所有库的加载情况,OS都非常清楚的!

进程地址空间第二讲:

什么是虚拟地址??什么是物理地址?

- 虚拟地址:是程序在编译、链接后生成的地址,属于进程地址空间中的地址,并非实际物理内存的地址。每个进程都有自己独立的虚拟地址空间,这样可以让多个进程在运行时互不干扰,也方便操作系统进行内存管理。
- 物理地址:是实际物理内存中的地址,是CPU最终要访问的内存地址,用于定位物理内存中的具体存储单元。

CPU读到的指令里的地址
CPU读到的指令里面用的是虚拟地址。因为程序运行时是基于虚拟地址空间的,之后会由操作系统通过内存管理单元(MMU)将虚拟地址转换为物理地址,从而实现对物理内存的访问。

现在,我们来谈谈关于地址的知识:

关于地址分为:

1.程序没有加载前的地址(程序)

2.程序加载后的地址(进程)。

我们先来第一个:

1.程序没有加载前的地址(程序)

(在程序没有被加载进来后,造成缺页中断,形成了天然的物理地址,所以页表可立马填上,虚拟地址与物理地址对应!)

程序编译好了之后,内部也有地址的概念。

平坦模式就是内存地址空间的管理模式,将虚拟地址空间视为连续的0-4GB区间,程序的各个段在这个虚拟地址空间中被分配相应的区域,便于程序的链接和加载。

在程序的地址空间中,entry入口地址是程序执行的起始地址,即程序加载到内存后,CPU开始执行指令的第一个地址。
通常,这个地址属于 .code 段(代码段),是程序逻辑执行的起点,比如 main 函数的入口(在一些程序中,入口地址会先指向初始化代码,再跳转到 main )。操作系统会将程序加载到内存后,把CPU的指令指针设置到这个入口地址,从而启动程序的执行流程。

2.程序加载后的地址(进程)

磁盘的可执行程序加载到内存时,涉及到查找这个文件。

根据路径找到在哪个目录下,根据目录,找到对应可执行程序它的inode,磁盘中哪个分区,分组下,读出它的属性,加载到物理内存。

(在上篇具体流程已经讲过了)

动态库的地址

再来想一下,共享内存大了,具体映射到哪里呢?动态库被加载到固定地址空间中的位置是不可能的!Linux中,动态库是放在虚拟内存中,可以任意位置加载!

关于位置无关码与静态库的解析
1. fPIC(位置无关码)
- 定义:是一种代码生成技术,生成的代码不依赖于特定的内存加载地址,可在内存的任意位置执行。
- 原理:通过偏移量对库中函数进行编址,使得代码在加载时无需重定位,能直接在不同内存位置运行,常用于共享库(如.so文件)的构建。(这个我们上面的动态库里也使用到了)
2. 静态库为何不谈加载和位置无关
- 静态库的工作机制:在编译链接阶段,会被完整地嵌入到可执行文件中。程序运行时,这些代码已经是可执行文件的一部分,不存在“加载”到内存某一区域的动态过程。
- 位置无关的必要性:由于静态库的代码直接合并到可执行文件,其地址在链接时就已确定,无需具备位置无关性,所以不需要考虑像共享库那样的位置无关问题。

补充知识:(知道有这个概念,以后再学习)

LRU 是 Least Recently Used(最近最少使用) 的缩写,是一种广泛用于缓存、内存管理等场景的资源淘汰算法。
其核心逻辑是:当系统需要释放资源(如缓存空间不足、内存满)时,会优先淘汰最长时间没有被访问或使用过的资源,以保留更可能被后续访问的“热点”资源,从而提升整体效率。
常见应用场景
- 缓存系统:如 Redis 的 LRU 缓存策略、浏览器缓存(淘汰长时间未访问的网页资源)。
- 内存管理:操作系统中用于页面置换(当物理内存不足时,淘汰最少使用的内存页面)。
- 数据库:部分数据库的查询结果缓存也会采用 LRU 策略。
例如:手机后台APP管理,当后台APP数量达上限时,系统会优先关闭“最久没点开过”的APP,这就是 LRU 逻辑的简化应用。

好了,关于Linux的基础IO知识就分享到这里完毕了,希望大家一起进步!

最后,到了本次鸡汤环节:

冲破这枷锁,挣脱这苦海

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值