当前位置: 首页 > news >正文

【Linux操作系统】简学深悟启示录:动静态库

文章目录

  • 1.文件系统补充
    • 1.1 内存硬盘交互
    • 1.2 系统对内存的管理
    • 1.3 文件的刷新
  • 2.动静态库
    • 2.1 静态库自制
    • 2.2 执行静态库
    • 2.3 动态库自制
    • 2.4 执行动态库
  • 希望读者们多多三连支持
  • 小编会继续更新
  • 你们的鼓励就是我前进的动力!

1.文件系统补充

1.1 内存硬盘交互

在这里插入图片描述

物理内存与硬盘交互数据时,通常是以 4KB 为单位的,物理内存的每一单位叫页框,硬盘的每一单位叫页帧,但是为什么是以 4KB 为单位,多次一点点传输不好吗,省得浪费空间?

其实设计者也是考虑了很多的,硬盘每次获取数据都要磁头定位,为了提高访问的效率,因此要尽可能减少 IO 次数,再就是当你要获取一块数据时,通常该数据附近的数据后续也很大概率要调用,所以不如一次性都调用出来,即预加载机制

1.2 系统对内存的管理

在这里插入图片描述

通常每一个 4KB 的页框就是一个结构体 page,把这每一个 page 简化成一整个数组,对内存的管理就变成了对数组的管理,每当需要取一个页框的数据时,就对物理地址进行特定的计算获取数组下标即可访问

1.3 文件的刷新

在这里插入图片描述

前面我们学习知道进程地址空间会找到 struct file获取文件数据,但是实际上后续更详细的操作还有待了解

struct file 本身不存储文件名,仅仅包含少量的文件属性,它通过 f_path.dentry 成员指向对应的 struct dentry(目录项),而 dentry 中会记录文件名(d_name 成员),同时 dentry 又通过 d_inode 成员指向 struct inode。简单说,三者的关联链是:struct filedentry(含文件名)→ struct inode,顺着这条链就能找到文件名与 inode 的对应关系,进而 struct inode 就能够获取更详细的文件属性

当通过 struct file 读写文件内容时,内核会先通过 file->f_inode->i_mapping 找到 address_space。检查页缓存中是否已有目标数据(通过 address_space 的页树查找)。
如果缓存命中,直接从内存页读取数据;如果未命中,address_space 的操作方法(a_ops->readpage)会触发从磁盘加载数据到缓存页,再返回给用户。
写操作也类似,通常先写入页缓存(标记为 “脏页”),后续由内核线程通过 address_space 的同步方法(如 a_ops->writepage)刷新到磁盘

如果把 “页缓存”(物理内存里的一块空间) 比作 “存放文件数据的仓库”,那么 struct address_space 就是 “仓库管理员”—— 它手里有 “仓库货位表”(page_tree),知道 “哪批数据(文件偏移)放在哪个货位(内存页)”;还负责 “进货”(从磁盘加载数据到仓库)、“出货”(把仓库数据写回磁盘),同时知道 “这个仓库属于哪个文件”(关联 inode

简单来说,就记住内核从 inode 结构体查找文件属性,从文件缓冲区查找文件内容

2.动静态库

有时我们需要通过引入第三方库满足自己的开发需求,libxxx.a 叫做静态链接,libxxx.so 叫做动态链接

2.1 静态库自制

站在发布者的角度,当我们自制一个静态库时,有两种方法供他人使用,一种是直接将全部源代码发布给别人使用,另一种方法就是打包成库,库=库+.h,这个 .h 就相当于说明书,告诉别人怎么使用,这种方法能很好的保护源代码最常用

以下将实现一个加减乘除的自制静态库

mymath.h

#pragma once#include <stdio.h>extern int myerrno;int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);

mymath.c

#include "mymath.h"int myerrno = 0;int add(int x, int y)
{return x + y;
}int sub(int x, int y)
{return x - y;
}int mul(int x, int y)
{return x * y;
}int div(int x, int y)
{if(y == 0){myerrno = 1;return -1;}return x / y;
}

Makefile

lib=libmymath.a$(lib):mymath.oar -rc $@ $^
mymath.o:mymath.cgcc -c $^.PHONY:clean
clean:rm -f *.o *.a lib.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib

变量 lib 依赖于 libmymath.a 静态库文件,$(lib) 表示引用该变量(即 libmymath.a),ar -rc $@ $^表示将 .o 文件全部归档打包成库,-rc 表示 replacecreat,打包成的库即有则覆盖无则创建,然后将所有 .c 文件编译成 .o 文件,最后等待用户一起连接即可,为什么要等待用户呢?

在这里插入图片描述

反正库里那么多文件都要编译,不如都编译成 .o 文件,因为太多了就打包成 libxxx.a 这样的库,等 main.c 编译成 .o 文件,再一起链接生成可执行文件

output 表示发布该静态库,将所有的 .h.o 文件整理成一个静态库文件方便发布

2.2 执行静态库

现在我们站在使用者的角度来使用该静态库

main.c

#include "mymath.h"int main()
{int ret = div(10, 0);printf("10 / 0 = %d, errno: %d\n", ret, myerrno);return 0;
}

首先将库 lib 放到和 main.c 相同的路径下,gcc 编译后发现报错这是为什么呢?

在这里插入图片描述

这是因为当库进行链接的时候,默认在系统路径下或当前目录下查找,但是对应的库和 .h 文件都在 lib 里面,系统找不到,此时就需要指定查找

gcc main.c -I lib/include/ -L lib/mymathlib -lmymath
  • -I lib/include/: 告诉编译器在 lib/include/ 目录下查找头文件 .h
  • -L lib/mymathlib: 告诉链接器在 lib/mymathlib 目录下查找库文件 .a
  • -lmymath: 指定要链接的静态库名称(实际对应的库文件是 libmymath.a-l 选项后省略 lib 前缀和 .a 后缀,注意不能有空格,依赖多个库就多个 -l 指定即可)

🔥值得注意的是: 除了直接执行命令以外,还可以通过将库的路径加入系统路径,或者在系统路径下创建软连接,但是依然需要 gcc main.c -lmymath 指定静态库文件

2.3 动态库自制

myprint.h

#pragma once#include <stdio.h>void Print();

myprint.c

#include "myprint.h"void Print()
{printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");
}

Makefile

dy-lib=libmymethod.so$(dy-lib):myprint.ogcc -shared -o $@ $^
myprint.o:myprint.cgcc -fPIC -c $^.PHONY:clean
clean:rm -rf *.o *.so mylib.PHONY:output
output:mkdir -p mylib/includemkdir -p mylib/libcp *.h mylib/includecp *.so mylib/lib

gcc -fPIC -c $^ 表示将 .c 文件编译为 .o 文件,-fPIC 是与位置无关码,简单来说就是不依赖于具体内存加载地址,而是计算相对位置的机器码,后续会详细介绍,-shared 表示生成动态库

2.4 执行动态库

现在我们站在使用者的角度来使用该动态库

gcc main.c  -I mylib/include -L mylib/lib -lmymethod

同样执行和静态库一样的编译 main.c 的命令

zzh@iv-ye51qmh4owxjd1uhrjzq:~/libraries/test$ ldd a.outlinux-vdso.so.1 (0x00007ffe01f82000)libmymethod.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f662ece1000)/lib64/ld-linux-x86-64.so.2 (0x00007f662ef17000)

发现依旧无法执行,执行 ldd a.out 命令查看可执行文件的状态,发现系统依旧无法找到动态库,这是因为编译器知道了动态库在哪,但是加载器还不知道啊,静态库在编译的时候把源码都包含进去了所以不需要额外加载,该有的代码都在里面了,而动态库需要到共享区去执行代码,所以要用到加载器

解决加载找不到动态库的四种方法:请添加图片描述

  1. 拷贝到系统默认的库路径 /usr/lib64/
  2. 在系统默认的库路径 /usr/lib64/ 下建立软连接
  3. 将自己的库所在的路径,添加到系统的环境变量 LD_LIBRARY_PATH
  4. /etc/ld.so.conf.d 建立自己的动态库路径的配置文件,然后重新 ldconfig 即可

🔥值得注意的是: 通常第一种用的最多也是最方便的,在 centos 下拷贝到系统默认路径下即可,Ubuntu/Debian 系列还需要在 /etc/ld.so.conf.d 配置 /usr/lib64 路径的 config 文件,不然系统找不到,这是因为不同系统的配置逻辑不同,centos 天生认识 /usr/lib64,但 Ubuntu/Debian 不把 /usr/lib64 当默认路径


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

http://www.dtcms.com/a/422715.html

相关文章:

  • 网站搭建设计筑龙网怎么免费下载
  • 网站制作哪家好网站建设中期目标
  • 前端开发时npm install报错解决方案
  • C#中 单线程使用 CancellationTokenSource 进行线程管理
  • .NET Core项目中 Serilog日志文件配置
  • 哈尔滨网站开发培训百度seo站长工具
  • 九江建设网站公司中信建设有限责任公司集采
  • DynImg论文阅读
  • 适合推广的网站wordpress自动标签加链接
  • ChatBI的相关学习
  • 【常用的git命令】
  • SNK施努卡汽车一体式天幕生产线
  • Celery时区设置问题源码探究
  • 音元分析流程
  • 懂的建设网站上海做网站优化
  • OpenLayers的OGC服务 -- 章节一:WMS服务详解
  • [信号与系统个人笔记]第三章 连续时间信号与系统的频域分析 Part 4
  • 多渠道打包gradle配置
  • 集中式架构还是分布式架构?SCADA架构选型的新趋势
  • 第八章 财务报表 2利润表(2025版)
  • 在Trae上使用Bright Data MCP采集数据,实时获取IPhone17价格信息
  • 番禺网站推广湖南网站建设有限公司
  • 刷题 | 牛客 - 前端面试手撕题 - 中等 - 1-2/20 知识点解答
  • 建立自动化SSL证书更新机制与多层监控体系
  • 岚图汽车 x Apache Doris : 海量车联网数据实时分析实践
  • chrome-devtools-mcp windows 环境安装
  • IOT_通讯控制器(IO模块)
  • 分布式计数器系统完整解决方案
  • 音频类AI工具扩展
  • PyCharm 开发 Python 项目后,将其打包并部署到 Nginx 服务器