195|2

475

帖子

0

TA的资源

纯净的硅(初级)

楼主
 

《Linux内核深度解析》--内存管理 [复制链接]

本帖最后由 dirty 于 2025-1-5 13:24 编辑

      本篇梳理阅读笔记学习内存管理章节。

内存管理架构

      内存管理架构分为用户空间、内核空间和硬件三个层面。

用户空间

      应用程序使用malloc()申请内存,使用free()释放内存。这个是我们很熟悉经常用到的,通常是配对使用。

内核空间

内核空间的基本功能

      虚拟内存管理负责从进程的虚拟地址空间分配虚拟页,sys_brk用来扩大或收缩堆,sys_mmap用来在内存映射区域分配虚拟页,sys_munmap用来释放虚拟页。内核使用延迟分配物理内存的策略。页分配器负责分配物理页,当前使用的页分配器是伙伴分配器。内核空间提供了把页划分成小内存块分配的块分配器,提供分配内存的接口kmaloc()和释放内存的接口 kfree(),支持3种块分配器:SLAB分配器、SLUB分配器和SLOP分配器。

内核空间的扩展功能

      不连续页分配器提供了分配内存的接口vmalloc和释放内存的接口vfee,在内存碎片化的时候,申请连续物理页的成功率很低,可以申请不连续的物理页,映射到连续的虚页,即虚拟地址连续而物理地址不连续。

硬件层面
      处理器包含一个称为内存管理单元(Memory ManagementUnit,MMU)的部件,负责把虚拟地址转换成物理地址。

 

内存映射
      内存映射是在进程的虚拟地址空间中创建一个映射,分为以下两种。通常把文件映射的物理页称为文件页,把匿名映射的物理页称为匿名页。

●文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。

●匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源。

      内存映射的原理如下。

①创建内存映射的时候,在进程的用户虚拟地址空间中分配一个虚拟内存区域。

②Linux内核采用延迟分配物理内存的策略,在进程第一次访问虚拟页的时候,产生缺页异常。如果是文件映射,那么分配物理页,把文件指定区间的数据读到物理页中,然后在页表中把虚拟页映射到物理页;如果是匿名映射,那么分配物理页,然后在页表中把虚拟页映射到物理页。

      内存管理子系统提供了以下常用的系统调用。

(1)mmap()用来创建内存映射。

(2)mremap()用来扩大或缩小已经存在的内存映射,可能同时移动。

(3)munmap()用来删除内存映射。

(4)brk()用来设置堆的上界。

(5)remap_file_pages()用来创建非线性的文件映射。

(6)mprotect()用来设置虚拟内存区域的访问权限。

(7)madvise()用来向内核提出内存使用的建议,应用程序告诉内核期望怎样使用指定的虚拟内存区域,以便内核可以选择合适的预读和缓存技术。

      在内核空间中可以使用以下两个函数。
(1)remap_pf_range把内存的物理页映射到进程的虚拟地址空间,这个函数的用处是实现进程和内核共享内存。

(2)io_remap_pfrange把外设寄存器的物理地址映射到进程的虚拟地址空间,进程可以直接访问外设寄存器。

      书籍这一章讲了差不多三百页,内存管理底层这块讲的也比较详细,面面俱到去梳理学习还是有些浩繁,有需要部分可以作为参考书籍重点查阅。作为Linux应用,了解下用户空间使用以及内核空间、硬件层面的基本原理 会加深对Linux内核的理解,当然,经常用得到的内存管理方面API还是应该熟记、熟用。

 

应用

     下面是mmap、munmap内存映射创建与解除应用代码。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>

int main() {
    int fd;
    struct stat sb;
    char *map, *p;
    int i;
    char c;

    // 打开文件
    fd = open("try.txt", O_RDWR);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 获取文件大小
    if (fstat(fd, &sb) == -1) {
        perror("fstat");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 将文件映射到内存
    map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
        perror("mmap");
        close(fd);
        exit(EXIT_FAILURE);
    }
    printf("mmap 将文件映射到内存 OK\r\n");
    // 关闭文件描述符,因为文件已经映射到内存
    close(fd);

    // 处理文件内容(例如,将所有字母转换为大写)
    p = map;
    for (i = 0; i < sb.st_size; i++) {
        c = *p;
        if (c >= 'a' && c <= 'z') {
            *p = c - 'a' + 'A';
        }
        p++;
    }

    printf("将所有字母转换为大写完成\r\n");

    // 解除映射
    if (munmap(map, sb.st_size) == -1) {
        perror("munmap");
        exit(EXIT_FAILURE);
    }
    printf("munmap 解除映射 OK\r\n");
    return 0;
}

      下面是运行情况,可以看到写入前文本小写字母转为大写字母,内存映射创建与解除均成功。

 

      本篇对内存管理有了初步梳理学习并小试牛刀,主要是学以致用,活学活用,有所取舍,后面继续学习与分享。

最新回复

Linux内核的内存管理本身就是比较复杂的玄学   详情 回复 发表于 昨天 07:35
点赞 关注(1)

回复
举报

6756

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

Linux内核的内存管理本身就是比较复杂的玄学

点评

是会有些复杂  详情 回复 发表于 昨天 21:24
 
 

回复

475

帖子

0

TA的资源

纯净的硅(初级)

板凳
 
Jacktang 发表于 2025-1-6 07:35 Linux内核的内存管理本身就是比较复杂的玄学

是会有些复杂

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表