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

Linux内核空间的布局

        目录

一、内核空间布局图

(1)线性映射区域(绿色)

(2).text、.data、.init(粉色系列)

(3)modules(深绿)

(4)PCI I/O(橙色)

(5)vmemmap(浅紫)

(6)vmalloc(浅紫)

(7)不规范地址(蓝色)

二、内核空间分配虚拟地址的函数

(1)kmalloc:物理地址连续的小内存

​​​​​​(2)vmalloc:物理地址不一定连续的大内存

(3)alloc_pages:按页分配连续的物理内存

三、用户空间分配虚拟内存的函数

1. brk:调整数据段(堆)的边界

2. sbrk:间接调整堆边界(库函数,基于brk)

3. mmap:映射内存区域(匿名映射 / 文件映射)

4.关于PCB中的结构体mm_struct

        我们知道所有的操作系统都会分为用户空间和内核空间,因为这样可以方便操作系统管理权限、用户进程等。但是关于内核空间真正的布局是什么样子呢?这篇文章我们就来仔细看看。

一、内核空间布局图

        在64位系统中,有着2^64次方个地址,即170多亿TB空间。但是真实的物理内存远远不可能这么大,所以操作系统中谈论地址一般都指的是虚拟地址,即进程误以为自己掌控了那么大的空间,实际上都是页表营造出来的假象罢了。

        即使在虚拟地址空间中,也要有一个规范的使用。于是普遍把低地址的空间给用户地址空间,高地址给内核使用,至于中间的部分则空缺出来不允许使用,也许随着技术的发展,内存空间极度扩大等原因,中间部分也能被利用起来。

而内核布局如下:

我们一一看看其作用:

(1)线性映射区域(绿色)
  • 作用:把 物理内存 直接映射到内核虚拟地址。简单说,内核想快速访问物理内存里的数据(比如硬件驱动读写内存)比如kmalloc、就依赖于此,用这里最方便。
  • 关键点:地址是 “线性” 对应的(虚拟地址和物理地址差个固定值),访问速度快,但会受物理内存大小限制。
(2).text、.data、.init(粉色系列)
  • .text(代码段):存内核的可执行代码(比如调度进程、处理系统调用的逻辑),只读!防止内核自己把代码改乱。
  • .data(数据段):存内核的全局变量、静态变量(比如记录系统总内存的变量)。
  • .init(初始化段):存内核启动时的初始化代码,启动完成后会被释放(节省内存)。
(3)modules(深绿)
  • 作用:加载内核模块(比如 USB 驱动、网卡驱动)的区域。驱动不需要编译进内核,用 insmod 动态加载到这里。这里就是我们之前所说的Linux借鉴了微内核插件式的特点。
(4)PCI I/O(橙色)
  • 作用:给 PCI 设备(比如显卡、网卡)的 I/O 地址用的。内核通过这里读写硬件寄存器,和外接设备通信。
(5)vmemmap(浅紫)
  • 作用:管理物理页框(Page Frame)的元数据。内核用它记录 “哪些物理内存被占用、哪些空闲”,类似一个 “内存台账”。
  • 物理内存中的每个页框(Page Frame)对应 vmemmap 中的一个struct page
(6)vmalloc(浅紫)
  • 作用:分配 虚拟地址连续、物理地址可能不连续 的内存。比如内核需要一大块内存,但物理内存零散,用 vmalloc 拼接虚拟地址。
  • 关联函数:内核里用 vmalloc() 分配,对应用户空间的 mmap(但机制不同)。
(7)不规范地址(蓝色)
  • 作用:“禁区”!内核和用户空间都不能用,防止程序乱访问地址导致崩溃。

二、内核空间分配虚拟地址的函数

(1)kmalloc:物理地址连续的小内存

作用:分配 物理地址连续 的内存,适合驱动里存小数据(比如设备结构体)。基于 slab 分配器,高效且少碎片。

        内核中管理的一些数据结构,各种设备的结构体节点,pcb等因为查询频繁,最好使用连续的物理内存,能提高查找效率和命中率

#include <linux/slab.h>
void driver_init() 
{struct device_data *data = kmalloc(sizeof(struct device_data), GFP_KERNEL);if (data) { /* 用内存 */ }kfree(data); // 释放
}

​​​​​​(2)vmalloc:物理地址不一定连续的大内存

作用:分配 虚拟地址连续、物理地址可能分散 的内存,适合需要大块内存但物理内存零散的场景(比如加载大模块)。

缺点:因为物理地址不连续,访问速度比 kmalloc 慢一点。

        一般在操作一些驱动设备模块的时候,需要较大的空间,并且对物理内存不要求连续,则往往会使用vmalloc。

(3)alloc_pages:按页分配连续的物理内存

作用:直接分配物理页框(比如 4KB 一页),返回页的虚拟地址。适合底层内存管理(比如文件缓存)。

        有些外设会使用DMA操作,提高读取效率,这时候按页分配连续的物理内存较为合适。

#include <linux/gfp.h>
struct page *pages = alloc_pages(GFP_KERNEL, 2); // 分配 2 页(8KB)
// ... 使用内存 ...
__free_pages(pages, 2); // 释放

三、用户空间分配虚拟内存的函数

1. brk:调整数据段(堆)的边界

  • 功能:通过修改进程数据段(堆)的结束地址(brk值),扩展或收缩堆空间。
    堆是从数据段末尾(end_data)向高地址增长的区域,brk系统调用直接控制堆的 “边界”。
  • 参数addr为新的堆结束地址(必须是页对齐的)。
  • 返回值
    • 成功:返回 0(表示堆已调整到addr);
    • 失败:返回 - 1(如内存不足)。
  • 适用场景:分配小内存块(堆内存),malloc对小内存的分配通常基于brk实现。
#include <unistd.h>
#include <stdio.h>
int main() 
{void *current_brk = sbrk(0);  // 获取当前brk值(sbrk是库函数,基于brk)void *new_brk = (char*)current_brk + 4096;  // 新地址(+4KB)if (brk(new_brk) == -1) {perror("brk failed");return 1;}printf("堆已扩展到:%p\n", new_brk);return 0;
}

2. sbrk:间接调整堆边界(库函数,基于brk

  • 说明sbrk不是系统调用,而是 glibc 提供的库函数,其底层通过brk系统调用实现,更方便地调整堆大小。
  • 参数increment为堆的增量(正数扩展,负数收缩,0 则返回当前brk值)。
    void *sbrk(intptr_t increment);
  • 返回值
    • 成功:返回调整前的brk值;
    • 失败:返回(void*)-1

3. mmap:映射内存区域(匿名映射 / 文件映射)

  • 功能:在进程地址空间中创建一个新的内存映射区域,可用于分配 “匿名内存”(不关联文件)或映射文件内容到内存。
    匿名映射是用户空间分配大内存块的主要方式(malloc对大内存通常用mmap)。
  • 适用场景:分配大内存块(通常malloc对大于 128KB 的内存用mmap,避免堆碎片)、文件映射。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

4.关于PCB中的结构体mm_struct

        进程的所有内存操作(堆、栈、映射区域)都通过mm_struct被内核跟踪和管理,确保地址空间的合法性和安全性。也就是说我们上述所有给用户空间分配内存的操作,本质都是在修改mm_struct的成员。

        用户空间的api,如brk、mmap、malloc本质都是在填充内核中mm_struct的某些字段,并不会当时就分配好内存,仅规划虚拟地址空间。而是真正要使用的时候,会触发缺页中断,再去查看mm_struct,最终调用到alloc_pages才算分配物理内存。

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

相关文章:

  • 前端面试专栏-工程化:26.性能优化方案(加载优化、渲染优化)
  • 《Qt5串口开发》搭建跨平台通信系统
  • “外卖大战”正在改变国内“大零售”
  • 数据增强和微调
  • Codeforces Round 1037 (Div. 3)
  • windows docker-02-docker 最常用的命令汇总
  • uniapp props、$ref、$emit、$parent、$child、$on
  • 【数据结构】栈(stack)
  • xss-labs1-8题
  • ubuntu24 ros2 jazzy
  • OpenVINO使用教程--图像增强算法DarkIR
  • 华为擎云L420安装LocalSend
  • Oracle为什么需要临时表?——让数据处理更灵活
  • LeetCode 322. 零钱兑换 LeetCode 279.完全平方数 LeetCode 139.单词拆分 多重背包基础 56. 携带矿石资源
  • 【补题】Codeforces Round 958 (Div. 2) D. The Omnipotent Monster Killer
  • 窗口(6)-QMessageBox
  • ctf.show-web习题-web4-flag获取详解、总结
  • 动态规划——状压DP经典题目
  • Weavefox 图片 1 比 1 生成前端源代码
  • 计算机网络:(十)虚拟专用网 VPN 和网络地址转换 NAT
  • 详细阐述 TCP、UDP、ICMPv4 和 ICMPv6 协议-以及防火墙端口原理优雅草卓伊凡
  • 【王树森推荐系统】推荐系统涨指标的方法04:多样性
  • sql练习二
  • 模型自信度提升:增强输出技巧
  • 《Spring Boot 插件化架构实战:从 SPI 到热插拔的三级跳》
  • 6. 装饰器模式
  • 教育科技内容平台的破局之路:从组织困境到 UGC 生态的构建
  • 我是怎么设计一个订单号生成策略的(库存系统)
  • 带root权限_新魔百和cm311-5_gk6323不分代工通刷优盘强刷及线刷
  • Openlayers 面试题及答案180道(141-160)