进程—内存描述符(mm_struct)

2023-05-16

转自:进程—内存描述符(mm_struct)_宇哲-CSDN博客_mm_struct,部分内容修改

前言

        上一篇我们谈论了task_struct这个结构体,它被叫做进程描述符,内部成员包含了很多与进程相关的信息,今天我们来看一下其中一个被叫做内存描述符的结构体——mm_struct,用来描述linux下进程的地址空间的所有的信息。

1.概述

        一个进程的虚拟地址空间主要由两个数据结来描述。一个是最高层次的:mm_struct,一个是较高层次的:vm_area_structs。最高层次的mm_struct结构描述了一个进程的整个虚拟地址空间。较高层次的结构vm_area_truct描述了虚拟地址空间的一个区间。每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有一个指向该进程的结构。可以说,mm_struct结构是对整个用户空间的描述。

        首先,我们来定位mm_struct文件所在位置和task_struct所在路径是一样的,不过他们所在文件是不一样的,mm_struct所在的文件是mm_types.h,接下来我们就来分析这个结构好了。

首先我们来看下这个:


这就是我们所说的由task_struct到mm_struct,进程的地址空间的分布。

 

        每一个进程都会有自己独立的mm_struct,这样每一个进程都会有自己独立的地址空间,这样才能互不干扰。当进程之间的地址空间被共享的时候,我们可以理解为这个时候是多个进程使用一份地址空间,这就是线程。

        其实多个进程的地址空间分布就是上面这张图一样,每一个进程的用户空间在32位的平台上就是上面这个图的情况,对于物理内存当中的内核kernel,是只存在一份,所有的进程是用来共享的,内核当中会利用PCB(进程控制块)来管理不同的进程。对于linux的体系结构来说,linux当中为了保护虚拟内核空间不被修改,所以linux体系结构是这样的:

        这种三层的体系结构,保证进程只能对最外面的应用程序进行修改,保证了内存的安全性。

        另外,我们从第一张图上可以发现,每个区域是依靠着两个指针进行维护的,比如[start_data,end_data)是用来维护data段,[start_code,end_data)用来维护code段,[start_brk,brk),用来维护heap和heap的指针。[start_stack,end_stack)是用来维护stack段空间范围。mmap_base是维护共享映射区的起始地址。bss段表示的是所有的未初始化的全局变量,为了效率,对处在bss段的变量,将它们匿名映射到“零页”,这样提高了程序的加载效率。

    //指向线性区对象的链表头
    struct vm_area_struct * mmap;       /* list of VMAs */
    //指向线性区对象的红黑树
    struct rb_root mm_rb;

        在地址空间中,mmap为地址空间的内存区域(用vm_area_struct结构来表示)链表,mm_rb用红黑树来存储,链表表示起来更加方便,红黑树表示起来更加方便查找。区别是,当虚拟区较少的时候,这个时候采用单链表,由mmap指向这个链表,当虚拟区多时此时采用红黑树的结构,由mm_rb指向这棵红黑树。这样就可以在大量数据的时候效率更高。所有的mm_struct结构体通过自身的mm_list域链接在一个双向链表上,该链表的首元素是init_mm内存描述符,代表init进程的地址空间。

    atomic_t mm_users;      
    atomic_t mm_count;

这两个内容表示的各有不同。

成员内容
mm_users进程数量值(在多线程的情况下尤为适用)
mm_count引用计数(当计数为0的时候表示没有再被使用)

使用mm_users和mm_count两个计数器是为了区别主使用计数器和使用该地址空间的进程的数目。

每一个进程都可以被别的进程来共享,也就是和别的进程来共享mm_struct.

所有的mm_struct结构以链表的形式存在mm_struct的。

        另外需要说明的就是kernel线程是没有地址空间的,也就没有对应的mm_struct,kernel线程使用之前运行的进程的内存描述符。

        程序中通常用到的地址常常具有局部性,当前最近一次用蛋糕的虚拟地址区间很可能下一次还是需要用到,所以我们采用局部性原理,通常时候我们去吧当前地址周围一个区间的内存放入高速缓存当中,这个区间在mm_struct当中就是由mmap_cache来维护。

2.关于页表

        linux kernel 使用内存管理的时候,采取的是页式的管理方式,应用程序给出的内存地址是虚拟地址,是经过若干层的页表的转换才能得到真正的物理地址,所以相对来说,进程的地址空间是一份虚拟的地址空间,每一个地址通过页表的转换映射到所谓的物理地址空间上。在这里所共享的1G的kernel在内存地址是只存一份的,但是对于每一个进程其他的3G的空间,是存储其他不同的东西,另外,页表具有权限限定,这样也就提供给了每块内存区域,比如我定义了:

char * p="12342";

这里的“12342”是一个常量字符串,它被存放在只读常量存储区,所以这个区域的页表的属性就是只读,这样就可以高效的维护整个进程的地址空间。

每一个进程都会有一个进程描述符,task_struct,task_strust当中的mm指针指向每个进程的内存描述符,而对于每个mm,有都会有单独的页表,

pgt区间是用来维护页表的目录,每一个进程的都有自己的页表目录,需要注意进程的页目录和内核的页目录是不一样的,当程序调度器调度程序运行的时候,这个时候这个地址就会转换成为物理地址,linux一般采用三级页表进行转换。

3.task_struct和mm_strcuct的联系
不知道你是否还记得在task_struct当中的

    //关于进程的地址空间,指向进程的地址空间。(链表和红黑树)
    struct mm_struct *mm, *active_mm;

ask_struct和mm_strcut通过这两个成员进行和mm_struct联系,每一个进程都会有唯一的mm_struct结构体。




struct mm_struct {

    //指向线性区对象的链表头
    struct vm_area_struct * mmap;       /* list of VMAs */
    //指向线性区对象的红黑树
    struct rb_root mm_rb;
    //指向最近找到的虚拟区间
    struct vm_area_struct * mmap_cache; /* last find_vma result */

    //用来在进程地址空间中搜索有效的进程地址空间的函数
    unsigned long (*get_unmapped_area) (struct file *filp,
                unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags);

       unsigned long (*get_unmapped_exec_area) (struct file *filp,
                unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags);

    //释放线性区时调用的方法,          
    void (*unmap_area) (struct mm_struct *mm, unsigned long addr);

    //标识第一个分配文件内存映射的线性地址
    unsigned long mmap_base;        /* base of mmap area */


    unsigned long task_size;        /* size of task vm space */
    /*
     * RHEL6 special for bug 790921: this same variable can mean
     * two different things. If sysctl_unmap_area_factor is zero,
     * this means the largest hole below free_area_cache. If the
     * sysctl is set to a positive value, this variable is used
     * to count how much memory has been munmapped from this process
     * since the last time free_area_cache was reset back to mmap_base.
     * This is ugly, but necessary to preserve kABI.
     */
    unsigned long cached_hole_size;

    //内核进程搜索进程地址空间中线性地址的空间空间
    unsigned long free_area_cache;      /* first hole of size cached_hole_size or larger */

    //指向页表的目录
    pgd_t * pgd;

    //共享进程时的个数
    atomic_t mm_users;          /* How many users with user space? */

    //内存描述符的主使用计数器,采用引用计数的原理,当为0时代表无用户再次使用
    atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */

    //线性区的个数
    int map_count;              /* number of VMAs */

    struct rw_semaphore mmap_sem;

    //保护任务页表和引用计数的锁
    spinlock_t page_table_lock;     /* Protects page tables and some counters */

    //mm_struct结构,第一个成员就是初始化的mm_struct结构,
    struct list_head mmlist;        /* List of maybe swapped mm's.  These are globally strung
                         * together off init_mm.mmlist, and are protected
                         * by mmlist_lock
                         */

    /* Special counters, in some configurations protected by the
     * page_table_lock, in other configurations by being atomic.
     */

    mm_counter_t _file_rss;
    mm_counter_t _anon_rss;
    mm_counter_t _swap_usage;

    //进程拥有的最大页表数目
    unsigned long hiwater_rss;  /* High-watermark of RSS usage */、
    //进程线性区的最大页表数目
    unsigned long hiwater_vm;   /* High-water virtual memory usage */

    //进程地址空间的大小,锁住无法换页的个数,共享文件内存映射的页数,可执行内存映射中的页数
    unsigned long total_vm, locked_vm, shared_vm, exec_vm;
    //用户态堆栈的页数,
    unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
    //维护代码段和数据段
    unsigned long start_code, end_code, start_data, end_data;
    //维护堆和栈
    unsigned long start_brk, brk, start_stack;
    //维护命令行参数,命令行参数的起始地址和最后地址,以及环境变量的起始地址和最后地址
    unsigned long arg_start, arg_end, env_start, env_end;

    unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

    struct linux_binfmt *binfmt;

    cpumask_t cpu_vm_mask;

    /* Architecture-specific MM context */
    mm_context_t context;

    /* Swap token stuff */
    /*
     * Last value of global fault stamp as seen by this process.
     * In other words, this value gives an indication of how long
     * it has been since this task got the token.
     * Look at mm/thrash.c
     */
    unsigned int faultstamp;
    unsigned int token_priority;
    unsigned int last_interval;

    //线性区的默认访问标志
    unsigned long flags; /* Must use atomic bitops to access the bits */

    struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO
    spinlock_t      ioctx_lock;
    struct hlist_head   ioctx_list;
#endif
#ifdef CONFIG_MM_OWNER
    /*
     * "owner" points to a task that is regarded as the canonical
     * user/owner of this mm. All of the following must be true in
     * order for it to be changed:
     *
     * current == mm->owner
     * current->mm != mm
     * new_owner->mm == mm
     * new_owner->alloc_lock is held
     */
    struct task_struct *owner;
#endif

#ifdef CONFIG_PROC_FS
    /* store ref to file /proc/<pid>/exe symlink points to */
    struct file *exe_file;
    unsigned long num_exe_file_vmas;
#endif
#ifdef CONFIG_MMU_NOTIFIER
    struct mmu_notifier_mm *mmu_notifier_mm;
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
    pgtable_t pmd_huge_pte; /* protected by page_table_lock */
#endif
    /* reserved for Red Hat */
#ifdef __GENKSYMS__
    unsigned long rh_reserved[2];
#else
    /* How many tasks sharing this mm are OOM_DISABLE */
    union {
        unsigned long rh_reserved_aux;
        atomic_t oom_disable_count;
    };

    /* base of lib map area (ASCII armour) */
    unsigned long shlib_base;
#endif
};

github注释源码链接:https://github.com/wsy081414/C_linux_practice/blob/master/mm_struct.c

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程—内存描述符(mm_struct) 的相关文章

  • ubuntu如何分区

    1 swap交换分区 xff0c 一般为你机器内存的两倍 xff0c 少于这个容量 xff0c 系统无法进入休眠 实质是硬盘上的交换空间而非分区 xff0c 所以没有格式 xff0c 默认休眠将数据储存于此 可以取消 xff08 如不用sw
  • git-cola安装与使用

    64 git cola安装与使用 linux下可视化git工具git cola安装与使用 xff08 SSH方式 xff09 链接 https blog csdn net zyhse article details 108813116
  • 家庭百兆升级千兆全攻略

    近日电信把家庭宽带给自动升成了300M xff0c 但奈何家里硬件限制 xff0c 一直都无法享受超快的速度 于是乎 xff0c 只有撸起袖子自己干 xff0c 下面来看看我的踩坑之旅吧 材料准备 千兆光猫6类以上网线 xff0c 最好带屏
  • ubuntu18.04 使用USB串口调试

    1 环境ubuntu18 04 安装了minicom环境 如果没有安装 xff0c 在执行minicom命令时会提示安装 step1 查看连接串口 gt 执行 ls dev tty Tab按键 目标串口 ttyUSB0 ttyUSB1 这个
  • STL详解及常见面试题

    文章目录 一 STL的介绍二 空间配置器详解1 第一级配置器详解2 第二级空间配置器详解3 空间配置器存在的问题 三 各种容器的特点和适用情况四 各种容器的底层机制和常见面试题1 vector xff08 1 xff09 vector的底层
  • VR中的9轴传感器(重力加速度/陀螺仪/磁力计).md

    前言 传感器的调试过程 xff0c 一般根据原厂提供demo代码 xff0c 调试数据接口 xff0c 将数据流打通即可 xff0c 在VR中 xff0c 当带上头显设备 xff0c 运行应用时 xff0c 出现漂移 延迟 不回归问题 xf
  • 数字信号处理 --- 信号分解基础

    信号的分解 重剑无锋 xff0c 大巧不工 信号的分解方式很多 xff0c 大家最常用也最熟知的就是傅里叶变换了 xff0c 然而有很多非常基础的分解方式往往不为人所知 他们的目的都是以某种方法去完美的分解并重建 还原信号 xff0c 闲来
  • leetcode系列-字符串

    字符串 xff1a 总结篇 从字符串的定义到库函数的使用原则 xff0c 从各种反转到KMP算法 什么是字符串 字符串是若干字符组成的有限序列 xff0c 也可以理解为是一个字符数组 反转系列 344 反转字符串 编写一个函数 xff0c
  • CPU种类

    CISC 1 英特尔处理器 xff1a 奔腾 赛扬 酷睿 至强 其中奔腾和赛扬系列定位低端 xff0c 酷睿系列又细分为酷睿i3 i5 i7 分别代表中端 中高端 高端 至强系列主要应用为服务器处理器 奔腾双核 xff0c 赛扬双核 xff
  • “段错误 (核心已转储) ”一种可能原因及其解决方法

    终端在运行的时候总是出现 段错误 核心已转储 栈空间用来存储数组等数据 xff0c 那么段错误就应该是我存储的数组超过了它所在段的大小 xff0c 于是在的程序执行的过程中一到跟大数组相关的步骤就会出现段错误的提示 xff08 SIGSEG
  • 统一javaweb项目和mysql数据库时间UTC时间方法及原理

    统一javaweb项目和mysql数据库时间UTC时间方法及原理 文章目录 统一javaweb项目和mysql数据库时间UTC时间方法及原理 前言UTC时间与 GMT时间时间戳和时区 mysql时间字段解读4种日期类型比如datetime和
  • 【读书笔记】《视觉SLAM十四讲(高翔著)》 第11讲

    文章目录 1 安装gtsam2 程序编译3 程序运行4 用g2o viewer打开 g2o文件 本博客的内容是本章程序编译运行方法 xff0c 记录调通本章程序的过程 处理遇到报错的解决方法 本章程序的详细解析可参考robinhjwy的CS
  • pixhawk: px4代码初学分析:追溯电机控制--pwm输出

    追溯电机控制 pwm输出 正常工作状态下pwm输出过程简述 xff1a 其他状态下pwm输出 xff1a 正常工作状态下pwm输出过程简述 xff1a 姿态解算部分得出姿态控制量通过px4io cpp把姿态控制量发送给IOIO串口读取姿态控
  • feign 调用

    关于feign调用问题 1 引入pom依赖 注释的这个依赖别用 xff0c 这个虽然不报错 xff0c 也好像可以使用 xff0c 但实际是有bug的 xff0c 会导致无法注入bean span class token operator
  • Freemarker 获取controller 传过来的值

    var val 61 entity propertyRight
  • jpa 中的联表查询

    jpa 中的联表查询 ConstructorExpression span class token generics function span class token punctuation lt span SafeOccupationa
  • LocalDate 获取当前月得第一天和最后一天

    span class token keyword public span span class token keyword static span span class token keyword void span span class
  • layui iframe 自定义按钮关闭当前窗口

    1 定义一个button 2 实现方法 formClose 61 function var index 61 parent layer getFrameIndex window name 先得到当前iframe层的索引 parent lay
  • Centos7安装Docker

    0 安装Docker Docker 分为 CE 和 EE 两大版本 CE 即社区版 xff08 免费 xff0c 支持周期 7 个月 xff09 xff0c EE 即企业版 xff0c 强调安全 xff0c 付费使用 xff0c 支持周期
  • @Async 使用和失效情况

    64 Async 使用和失效情况 因业务需要 xff0c 经常会遇到主线程中包含其他关联业务 xff0c 然关联业务的执行结果对主线程的返回结果没有直接影响或无影响 此时 xff0c 能让主线程更顺畅的执行 xff0c 并给客户带来好的客户

随机推荐