• 8. 进程地址空间 20100221 15:52 553人阅读 评论(0) 收藏


    对于进程用户态分配内存请求,总被认为是不紧迫的,为提高效率采用推迟分配。进程请求动态

    内存时,并未获得请求的页框,而仅仅获得对一个新的地址区间的使用权。这个区间叫做线性区。即进程

    请求动态内存时,请求得到的是一个线性区,线性区被视为一种资源,用来组成线性地址区间,为效率起

    见,它一般是4096的倍数。显然,能确认一个进程当前拥有的线性区是内核的基本任务,完成此任务就必

    须要有相应的数据结构。
    首先,与进程的全部地址空间相关的信息包含在“内存描述符”中,进程描述符的mm字段指向它

    。类似于进程描述符,内存描述符也在全局上由双向链表串起。链表头是init_mm,它初始化是进程0使用

    的内存描述符。mm_users字段存放共享它的轻量级进程个数。mm_count是使用它的进程个数。要单独引入

    mm_count是因为内核线程可以借用其它进程的内存描述符,mm_users为0时释放局部描述符表、线性区描述

    符、页表;而当mm_count为0时,就要释放这个内存描述符本身占用内存。内核线程要借用其它进程的内存

    描述符是因为它不访问用户空间的线性地址,即它不使用线性区,这样如果给内核线程分配一个内存描述

    符会有很多字段用不到,同时又因为任何进程大于0xc0000000的线性地址是相同的,所以内核线程只要随

    便借一个进程的内存描述符就可以正常工作。为了支持这种“借”,进程描述符内除了mm还有一个

    active_mm字段指出进程当前正在使用的内存描述符。一般进程两者相等,而内核线程的mm为NULL。
    其次,由于用户态进程分配的不是页框,而是线性区,为描述这个重要的概念,必须引入线性区

    描述符来表示它。它有相关的字段指出线性区本身的起始、末尾及所属的内存描述符。反过来,内存描述

    符中也有mmap字段指向第一个线性区,并用mmap_cache字段指向刚访问过的线性区,用map_count指出其中

    线性区的数量。为管理方便,进程的线性区也用链表串起来,但当进程是诸如面向对象的数据库这样的拥

    有上千个线性区的进程,用mmap->vm_next找链表显然太慢。Linux2.6将线性区也组织在红黑树中加速查找

    。因为线性区的起始地址、大小都是4096的倍数,所以它们可由一组连续的页构成。对于页,页表项中有

    80x86硬件负责检查的一些权限,在页描述符中,有Linux用的一组权限标志,这里,在线性区描述符中,

    又有一组标志指出了相关权限,这里的保护方案是为缺页异常提供的,它规定了什么情况下的访问要产生

    缺页异常,而真正的访问权限的实现仍需要通过改变页表项,并且在一些设置下,可以访问但是仍会触发

    缺页异常。
    对数据结构了解后,就是一些对线性区处理的函数。如给定地址查找邻近或重叠的线性区。在查

    找一个空的地址敬意时要注意:用户态线性地址的三分之一是被保留用作可执行文件的正文段、数据段、

    bss段的,这样搜索时可避开第一个G,在查找时,为方便,直接从mmap_cache后开始查找。总的来说,分

    配线性区间时,先获得新线性区线性地址,再由slab分配函数为新线性区分配描述符并初始化,之后再插

    入内存描述符的mm_map和红黑树mm_rb。增加内存描述符的地址空间大小字段。释放时,扫描链表,从链表

    中将要删线性区脱开,更新进程页表,将第一阶段找到的线性区删除。
    80x86缺页异常处理程序必须区分各种访问情况,做出正确处理。流程较复杂,见P378流程图。
    开头提及的分配内存只分线性区,而将页框分配推后到不能推后为止的技术叫“请求调页”。它

    由上述的线性区描述符的访问权限配合缺页异常来实现。缺点是必须由内核处理缺页异常,浪费了CPU时钟

    周期。而局部性原理可以保证缺页异常发生频率不高,使“请求调页”工作良好。当缺页异常发生且线性

    区没有映射到磁盘文件,调用do_anonymous_page()获得页框。处理读时,更无关紧要,只要专门设一个0

    页让其读即可。
    第一代unix实现了一种傻瓜式的进程创建:直接在fork()时复制整个地址空间。这样太慢且完全

    破坏高速缓存内容。现今普遍采用写时复制(Copy On Write),它的思想就是不复制页框,而是将页框设

    为共享。此后无论父或子进程想写共享页框,会产生异常,在异常处理中将页复制到一个新页框,标记为

    可写,新页框的物理地址最终写入页表项,原页框页描述符_count字段减1,但仍是写保护,除非减1后已

    为-1,此时原页框会被释放。
    最后,关注进程地址空间的创建与删除:创建时,copy_mm()函数会建立新进程的页表与内存描述

    符,如前写时复制,普通进程共享父进程的地址空间,而轻量级进程直接使用它,这也是进程与线程概念

    上的本质区别,代码上可表示为:轻量级tsk->mm = current->mm; tsk->active_mm = current->mm;而普

    通进程则为tsk->mm = kmem_cache_alloc(mm_cache,SLAB_KERNEL); memcpy(tsk->mm,current-

    >mm,sizeof(大小));接下来普通进程会改变复制来的内存描述符的一些字段,最后复制父进程线性区与页

    表,将新内存描述符插入全局链表。复制来的线性区插入线性区链表与红黑树,初始化表项以便写时复制

    机制。终止进程时,若非内核线程,exit_mm()释放内存描述符和相关数据结构。

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Vue让水平滚动条(scroll bar)固定在浏览器的底部,并且同轴联动
    vue横向滚动条,初始化位置
    VUE父子组件传值,以及子组件调用父组件方法
    获取shell脚本所在路径而非执行路径
    免重装完整迁移ubuntu18.04系统方法
    auth.log大量出现pam_unix(cron:session): session opened for user root by (uid=0)解决办法
    禁用vim的visual模式方便拖选
    ssh端口反向代理与内网穿透
    mysql查询时将时间戳转换为时间格式
    浏览器打印控件分享
  • 原文地址:https://www.cnblogs.com/qqmomery/p/4700464.html
Copyright © 2020-2023  润新知