027 动静态库 —— 静态库

AI-摘要
小米里的大麦 GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
027 动静态库 —— 静态库
小米里的大麦动静态库 —— 静态库
1. 扩展概要(了解)
一个文件被打开操作系统要做什么:打开文件的流程 = 找 inode ➜ 创建 file 结构 ➜ 建立缓冲 ➜ 用页表映射读写 ➜ 后续 read/write 就操作 page 缓冲。
1. 为什么说“4KB Page”是核心单位?
现代操作系统使用 分页机制(Paging) 管理内存,每一页(Page)是 一块固定大小的内存单元。Linux 默认:
- 一页大小为 4KB。
- 物理内存也以页框为单位分配(如 Frame #0,Frame #1…)。
- 所有虚拟内存地址映射到物理内存页框时,以 4KB 为基本单位。
| 地址空间 | 页面大小 | 含义 |
|---|---|---|
| 虚拟地址空间 | 4KB/page | 每个进程有自己的虚拟地址空间 |
| 物理内存 | 4KB/frame | 物理内存被划分为一个个页框 |
| 页面表(Page Table) | 将虚拟页映射到物理页框 |
所以:所有读写、分配、预加载、换页,都是以“页”为单位处理的!
2. 局部性原理?为什么和 4KB 页有关?
局部性原理包括:
- 时间局部性(Temporal Locality):刚访问的数据,可能马上还会用。
- 空间局部性(Spatial Locality):访问了地址 A,可能马上会访问 A 附近的数据。
为什么用 4KB?
- 如果你访问了一个变量
a,操作系统会把 整个 4KB 页 都读进内存(或者缓存); - 这个 4KB 的空间里,很可能包含你接下来会访问的变量
b, c, d; - 这样可以极大提升性能:只需要一次 IO,就加载一大片“可能用到的数据”。
磁盘文件(假设 8KB):[0
4KB] [48KB] (2 个块)
- 内核发现
[0~4KB]不在页缓存中。触发磁盘读取,加载整个[0~4KB]块到内存(即使只访问 1 字节)。存入页缓存,供后续快速访问。(访问第 1 字节 ➜ 内核页缓存判断没有 ➜ 从磁盘读[0~4KB]➜ 存入页缓存)- 继续访问第 3KB 字节 ➜ 命中页缓存 ➜ 直接返回(
[0~4KB]已在页缓存中,直接返回数据(无需磁盘 IO)。)- 访问第 5KB 字节 ➜ 没有命中 ➜ 再从磁盘读
[4~8KB]([4~8KB]不在页缓存中,再次触发磁盘读取,加载整个[4~8KB]块到内存。)
| 问题 | 回答 |
|---|---|
| 打开 1KB 文件会读取多少? | 最少读取 4KB |
| 多出来 3KB 是什么? | 文件中后续的数据 |
| 是怎么加载的? | 从磁盘块读取并映射到页缓存 |
| 多出来的内容从哪加载? | 从磁盘上一次性读取 4KB 的块(block)加载进来 |
| 为什么这么设计? | 为了性能,利用局部性原理进行预读 |
| 如果文件只有 1KB 呢? | 剩下部分为空白,但页缓存仍然占用 4KB |
3. 文件打开后是否每次都从磁盘读取?
不是!Linux 有一个机制叫做:页缓存(Page Cache)。把文件内容按“页”读取后,缓存到内存中。
- 文件第一次打开时,会将所需部分 加载到内存页缓存(struct page)中。
- 之后对文件的
read、write操作,都直接在内存中进行,不再频繁访问磁盘。
4. 每个打开的文件都会分配什么?
| 对象 | 含义 |
|---|---|
struct file |
表示这次“打开” |
struct inode |
表示文件的“元数据”(公共) |
| 页缓存(Page) | 对应的文件内容存到内存的页中 |
| 缓冲区(Buffer) | 应用层的输入输出缓冲区 |
所以:每个打开的文件,不管在哪个进程中,都有自己的“打开实例”,但共享
inode和缓存页。
5. Linux 如何管理所有内存?
Linux 会维护一个大的 mem_map[] 数组,里面是所有 物理内存页的描述结构 struct page。
- 每当程序申请内存,都会通过虚拟地址查找页表,映射到
mem_map[i]对应的页框。 mem_map[i]就表示第 i 个物理页框的属性(是否空闲、是否缓存文件、是否在使用等)。
6. 操作系统能看到物理地址吗?
操作系统自己可以看见物理地址,但应用程序不行。
- 每个进程只能访问自己的 虚拟地址空间;
- 操作系统通过 页表机制 管理虚拟地址 → 物理地址的映射;
所以现代操作系统的内存与文件缓存管理,都是基于“4KB 页”为单位,结合“局部性原理”和“分页机制”来提高性能和效率。
2. 动静态库
1. 库的制作者 如何制作静态库?
步骤如下:
① 编写
.c实现和.h头文件:对外接口声明和函数的实现。② 使用
gcc -c编译为目标文件(.o):gcc -c MyStaticLibrary.c -o MyStaticLibrary.o。③ 使用
ar命令打包创建静态库.a:ar -rc My_static_library.a My_static_library.o。④ 组织输出目录(可选):
1 | mkdir -p lib/include lib/My_static_library_lib |
2. 静态库 demo
1. My_static_library.h (头文件)—— 对外接口声明
1 |
|
2. My_static_library.c(源文件)—— 函数定义 + 错误处理
1 |
|
3. Makefile 文件 —— 构建静态库的自动化脚本
1 | # 目标静态库名称 |
3. 库的使用者 如何使用静态库?
1. 假设目录结构如下:
1 | . |
2. test_main.c 示例:
1 |
|
编译命令说明:
1 | gcc -o test test_main.c -I.lib/include -L./lib/My_static_library_lib -l:My_static_library.a |
| 选项 | 作用 | 说明 |
|---|---|---|
-I.lib/include |
指定头文件搜索路径 | 确保 My_static_library.h 能被 #include 找到 |
-L./lib/My_static_library_lib |
指定库文件搜索路径 | . 表示当前目录(即 ./My_static_library.a) |
-l:My_static_library.a |
直接链接指定的 .a 文件 |
: 表示精确匹配文件名,直接指定库文件名,而非默认的 libxxx.a 格式 |
小技巧(调试辅助):
查看
.a文件里的内容:1
ar -t My_static_library.a
若静态库升级,记得重新编译目标文件和链接。
3. 总结:制作者 & 使用者视角速查表
| 角色 | 步骤 | 命令或说明 |
|---|---|---|
| 制作者 | 编写 Header/Source | My_static_library.h/My_static_library.c |
| 生成目标文件(.o) | gcc -c My_static_library.c -o My_static_library.o |
|
| 打包静态库(.a) | ar -rc My_static_library.a My_static_library.o |
|
| 分发库与头文件 | mkdir -p lib/include lib/lib && cp *.h lib/include/ && cp *.a lib/lib/ |
|
| 使用者 | 包含头文件 (include) | #include "My_static_library.h" |
| 编译链接形成可执行程序 | gcc -o test test_main.c -I.lib/include -L./lib/My_static_library_lib -l:My_static_library.a |
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果


















