精华内容
下载资源
问答
  • Linux中的虚拟地址物理地址内存管理方式详解
    2017-12-04 10:16:03

    Linux中的虚拟地址、物理地址和内存管理方式

            虚拟地址和物理地址以及内存管理方式:

    Linux中的虚拟地址、物理地址和内存管理方式(一)

    Linux中的虚拟地址、物理地址和内存管理方式(二)


            内核空间和用户空间:

    linux 用户空间与内核空间——高端内存详解

    linux内核空间和用户空间详解

    内核空间与用户空间


    更多相关内容
  • linux内核内存虚拟地址映射物理地址

    千次阅读 2021-11-26 00:58:50
    其中,所有进程的内核空间,关联的都是相同的物理内存。进程切换到内核态后,才可以访问内核空间内存。我们下面说到的分段只针对用户空间。 有两种方式管理虚拟地址与物理地址之间的关系。 1、段式管理(Segment)...

    先说明下什么是虚拟地址。

    Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。虚拟地址空间的内部又被分为内核空间和用户空间两部分。不同字长(也就是单个 CPU 指令可以处理数据的最大长度)的处理器,地址空间的范围也不同。

    「源码分析」linux内核内存虚拟地址映射物理地址

    其中,所有进程的内核空间,关联的都是相同的物理内存。进程切换到内核态后,才可以访问内核空间内存。我们下面说到的分段只针对用户空间。

    有两种方式管理虚拟地址与物理地址之间的关系。

    1、段式管理(Segment):由段选择子和段内偏移量找到物理地址。

    「源码分析」linux内核内存虚拟地址映射物理地址

    「源码分析」linux内核内存虚拟地址映射物理地址

    用户空间从低地址到高地址分别是五种不同的内存段。

    • 代码段(只读段),包括代码和常量等。
    • 数据段,包括全局变量等。
    • 堆,包括动态分配的内存,从低地址开始向上增长。
    • 文件映射段,包括动态库、共享内存等,从高地址开始向下增长。(本图没有画出)
    • 栈,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。

    分段容易出现碎片,内存交换效率低(不容易换出到磁盘)的问题。为了解决这两个问题,就出现了内存分页。

    2、页式管理(Paging):虚拟地址分为两部分,页号和页内偏移。

    「源码分析」linux内核内存虚拟地址映射物理地址

    「源码分析」linux内核内存虚拟地址映射物理地址

    MMU 规定了一个内存映射的最小单位,也就是页,通常是 4 KB 大小。为了解决页表项过多的问题,有多级页表和大页两种方式。

    并不是给进程的所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存。这叫程序的局部性原理。根据此原理,为了提高访问速度,MMU(Memory Manage Unit)里配有一个硬件:TLB(Translation Lookaside Buffer)。用于缓存进程常用页表。

    内存分段和内存分页并不是对立的,他们组合起来使用,通常称为段页式内存管理

    • 程序所使用的地址,称为逻辑地址;
    • 通过段式内存管理映射的地址,称为虚拟地址(线性地址);
    • 通过页式内存管理将线性地址映射成物理地址。

    「源码分析」linux内核内存虚拟地址映射物理地址

    展开全文
  • Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,...
  • Linux 内存管理 详解(虚拟内存,进程地址空间)

    千次阅读 多人点赞 2022-04-16 10:06:19
    Linux -操作系统内存管理存储系统存储器的层次结构Linux的内存管理物理内存物理内存管理虚拟内存虚拟地址空间(写时拷贝)和物理地址映射关系

    计算机硬件的五大组成部分为:控制器, 运算器, 存储器, 输入和输出设备。

    存储系统

    存储器:用来存放所有数据和程序的记忆部件,它的基本功能是按指定的地址存(写)入或者取(读)出信息。 计算机中的存储器可分成两大类:一类是内存储器,简称内存或主存;另一类是外存储器(辅助存储器),简称外存或辅存。 存储器由若干个存储单元组成,每个存储单元都有一个地址,计算机通过地址对存储单元进行读写。处理器可以直接访问内存但不能直接访问外存,需要通过相应的I/O设备才能使外存和内存交换。

    一个作业必须把它的程序和数据存放在内存(主存)中才能运行。在多道程序系统中有多个程序以及相关数据放入内存中。操作系统不但要管理,保护这些数据,操作系统本身也要存放在内存中运行。
    因此内存以及与存储器有关的管理时支持操作系统运行硬件环境中一个重要方面。

    存储器的层次结构

    在这里插入图片描述


    Linux的内存管理

    Linux 采用虚拟存储技术,虚拟地址空间可达到 4GB

    物理内存

    在这里插入图片描述

    寄存器: 具有与处理机相同的速度与CPU协调工作,故对寄存器的访问速度最快,但价格却十分昂贵,因此容量不可能做得很大。用于加速存储器的访问速度,如用寄存器存放操作数,或用作地址寄存器加快地址转换速度等。
    高速缓存: 根据程序执行的局部性原理将主存中一些经常访问的信息存放在高速缓存中,减少访问主存储器的次数,可大幅度提高程序执行速度。
    主存储器: 内存,保存进程运行时的程序和数据。CPU与外围设备交换的信息一般也依托于主存储器地址空间。为缓和主存储器的访问速度远低于CPU执行指令的速度,在计算机系统中引入了寄存器和高速缓存。
    磁盘: 将频繁使用的一部分磁盘数据和信息,暂时存放在磁盘缓存中,可减少访问磁盘的次数。它依托于固定磁盘,提供对主存储器存储空间的扩充,即利用主存中的存储空间,来暂存从磁盘中读/写入的信息。
    磁带: 这种介质经常用于磁盘的备份,并H可以保存作常大量的数据集。在访门磁带前,首先要把磁带装到磁带机上,可以人工安装也可用机器人安装(在大型数据库中通常安装有白动磁带处理设备)。然后,磁带町能还需要向前绕转以便读取所请求的数捌块。


    物理内存管理

    目前计算机系统有两种体系结构:

    一致性内存访问 UMA(Uniform Memory Access)也可以称为对称多处理器SMP(Symmetric Multi-Process)。意思是所有的处理器访问内存花费的时间是一样的。也可以理解整个内存只有一个node。(但事实上严格意义的UMA结构几乎不存在)

    非一致性内存访问 NUMA(Non-Uniform Memory Access)意思是内存被划分为各个node,访问一个node花费的时间取决于CPU离这个node的距离。每一个cpu内部有一个本地的node,访问本地node时间比访问其他node的速度快

    所以Linux为了对 NUMA 进行描述,从Linux2.4开始引入了存储节点,把访问时间相同的存储空间称为一个存储节点。进而 Linux 将物理内存划分为三个层次来管理:存储节点,管理区,页面。

    ZONE
    Linux 2.6把每个内存节点的物理内在划分为3个管理区(zone)。

    1. ZONE_ DMA: 可以用来DMA操作的页。 ( <16MB)
    2. ZONE_NORMAL :正常规则映射的页。 (16MB~896MB)
    3. ZONE_ HIGHMEM: 高内存地址的页,并不永久性映射。 ( >896MB)
      在这里插入图片描述

    页 page

    内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻址单位通常为字(甚至字节),但是,内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址的硬件)通常以页为单位进行处理。正因为如此,MMU以页(page) 大小为单位来管理系统中的页表(这也是页表名的来由)。从虚拟内存的角度来看,页就是最小单位。

    代表一个物理页,在内核中一个物理页用一个struct page表示。
    在这里插入图片描述

    首先,Linux维护一个页描述符数组,称为mem_map, 其中页描述符是page类型的,而且系统当中的每个物理贞框都有一个页描述符。 每个页描述符都有个指针,在页面非空闲时指向它所属的地址空间,另有一对指针可以使得它跟其他描述符形成双向链表,来记录所有的空闲页框和一些其他的城。下图中,页面150的页描述符包含一个到其所属地址空间的映射。页面70、 页山80、页面200是空闲的,它们是被链接在一起的。 页描述符的大小是32字节,因此整个mem_ map消耗了不到1 %的物理内存(对于4KB的页框)。
    因为物理内存被分成区域,所以Linux为每个区域维护一个区域描述符。区城描述符包含了每个区域中内存利用情况的信息。
    此外,区域描述符包含一个空闲区数组。该数组中的第i个元素标记了2i个空闲页的第一个块的第一个页捕述符。既然可能有多块2i个空闲页,Linux使 用页描述符的指针对把这些页面链接起来。这个信息在Linux的内存分配操作中使用。free_ areal0]标记所有仅由一 个页框组成的物理内任空闲区,现在指向页面70,三个空闲区当中的第一个。其他大小为一个页面的空闲块也可通过页描述符中的链到达。 —— 《现代操作系统第三版》 431页
    在这里插入图片描述


    虚拟内存

    虚拟寻址

    CPU通过生成一个虚拟地址(Virtual Address,VA)来访问主存,这个虚拟地址在被送到内存之前先转换成适当的物理地址。将一个虚拟地址转换为物理地址的任务叫做地址翻译( address translation)。 就像异常处理一样,地址翻译需要CPU硬件和操作系统之间的紧密合作。CPU芯片上叫做内存管理单元 MMU(Memory Management Unit,)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。
    在这里插入图片描述


    虚拟内存的管理是以进程为基础,每个进程都有各自的虚拟地址空间,每个进程的内核空间是所有进程所共享的。 在Linux中虚拟地址空间主要是由最高层的 mm_struct 和 较高层次的 vm_ area_ struct 结构来描述的。

    在这里插入图片描述
    任务结构中的一个条目指向mm_ struct, 它描述了虛拟内存的当前状态。我们感兴趣的
    两个字段是pgdmmap,其中pgd指向第一级页表(页全局目录)的基址,而mmap指向一个
    vm_ area_ struct( 区域结构)的链表,其中每个vm_ area_ struct 都描述了当前虛拟地址空.
    间的一个区域。当内核运行这个进程时,就将pgd存放在CR3控制寄存器中。
    为了我们的目的,一个具体区域的区域结构包含下面的字段:
    ●vmstart:指向这个区域的起始处。
    ●vm_end:指向这个区域的结束处。
    ●vmprot:描述这个区域内包含的所有页的读写许可权限。
    ●vm_ flags:描述这个区域内的页面是与其他进程共享的,还是这个进程私有的(还
    描述了其他一些信息)。
    ●vm next:指向链表中下一个区域结构。
    ——《深入理解计算机系统第三版》 531页




    虚拟地址空间

    程序:是静态的,一些预先编译好的指令和数据集合的文件。
    进程:是动态的是程序运行时的一个过程,是系统资源分配的独立基本单位。

    每个程序运行起来都有自己独立的虚拟地址空间,空间大小由CPU的位数决定
    在这里插入图片描述

    用户空间:
    (1)代码段.text:存放程序执行代码的一块内存区域。只读,代码段的头部还会包含一些只读的常数变量

    (2)数据段.data:存放程序中已初始化的全局变量和静态变量的一块内存区域。

    (3)BSS 段.bss:存放程序中未初始化的全局变量和静态变量的一块内存区域。

    (4) 堆区:程序运行时动态申请内存用。堆从地址向地址增长。

    (5) 栈区:存储局部变量,函数参数、返回值。栈从地址向地址增长。是一块连续的空间。

    (5)内存映射段:是是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

    内核空间:DMA区、常规区(NORMAL)、高位区(HIGHMEN)。


    —————————————————————————————————————
    ❏什么时候从用户态进入内核态:其中,系统调用是主动的,另外两种是被动的。
      a、系统调用。 (直接调用系统接口或通过库函数调用)
      b、异常。( 捕捉信号处理异常 signal
      c、设备中断。

    —————————————————————————————————————

    //  Linux 进程地址空间代码
    #include <stdio.h>
    #include <stdlib.h>
    
    int val = 100;
    int unval;
    int main(int argc, char *argv[], char *env[])
    {
        printf(" code address: %p\n ",main);    //代码区
        printf(" init address: %p\n",&val);      //已初始化数据区
        printf(" uninit address: %p\n",&unval);  //未初始化数据区
        char *heap = (char*)malloc(10);       
        printf(" heap address: %p\n",heap);     //堆区
        printf(" stack address: %p\n", &heap);     //栈区
    
        printf(" command line arg address: %p\n", argv[0]);
        printf(" command line arg address: %p\n", argv[argc-1]);
        printf(" env address: %p\n", env[0]);
        return 0;
    }
    

    运行结果:
    在这里插入图片描述



    通过父子进程输出相同地址,但变量值不同简单佐证进程使用的是虚拟地址

    (写时拷贝)

    //
    #include <stdio.h>
    #include <unistd.h>
    
    int g_val = 0;
    int main()
    {
        pid_t p = fork();  ///创建父子进程
        if(p < 0){
            perror("fork\n");
            return 0;
        }
        else if(p == 0){  //子进程
            g_val = 99;  //改变全局变量值
            printf("i am clild %d, g_val is : %d, in %p\n", getpid(), g_val, &g_val);
        }
        else{
            sleep(2);  //睡眠两秒
            printf("i am parent %d, g_val is : %d, in %p\n", getpid(), g_val, &g_val);
        }
        return 0;
    }
    

    在这里插入图片描述
    在这里插入图片描述
    上述代码发生写时拷贝(Copy On Write)
    fork()产生新进程速度很快,创建出的两个进程共享代码和数据,共用同一块写时拷贝的内存空间;两个进程可以所以读取内存,但当一方试图对内存进行修改时,内存就复制一份提供给修改方单独使用,此时父子进程通过各自的页表,指向不同的物理地址。 (原理如下图)

    在这里插入图片描述


    进程分配内存方式(堆空间):两个系统调用
    在这里插入图片描述
    1.int brk(void* addr)
    实际作用就是设置进程数据段的结束地址,即改变数据段大小。

    在这里插入图片描述2. int mmap(void* addr, size_t length)
    作用是向操作系统申请一段虚拟地址空间。(这块虚拟地址空间可以映射到某个文件),当它不在映射文件时我们又称这块空间为匿名空间(可以用来作为堆空间)

    在这里插入图片描述




    和物理地址映射关系

    首先了解一下以下几种地址概念
    linux内存管理—虚拟地址、逻辑地址、线性地址、物理地址的区别(这篇大佬博客讲的很详细,可以参考学习一下)


    逻辑地址(logical address ):段基址(段地址)和段内偏移量(偏移地址)

    包含在机器语言指令中用来指定一个操作数或一条指令的地址。这种寻址方式在80x86著名的分段结构中表现得尤为具体,它促使MS- DOS或Windows程序员把程序分成若干段。每一一个逻辑地址都由一个段(segment)和偏移量(ofiset或displacement)组成,偏移量指明了从段开始的地方到实际地址之间的距离。

    线性地址(linear address )(也称虚拟地址virtual address): 段基址(段地址)+段内偏移量(偏移地址)
    当程序加载到内存运行,就从逻辑地址转变为虚拟地址,也就是CPU所看到的地址

    是一个32位无符号整数,可以用来表示高达4GB的地址,也就是,高达4 294 967
    296个内存单元。线性地址通常用土六进制数字表示,值的范围从0x0000000到
    0xfffffff.

    注意:与段相关的线性地址从0开始,达到232-1 的寻址限长。这就意味着在用户态或内
    核态下的所有进程可以使用相同的逻辑地址。
    所有段都从00000000开始,这可以得出另一个重要结论,那就是在Linux下逻辑地址
    与线性地址是一致的
    ,即逻辑地址的偏移量字段的值与相应的线性地址的值总是一致的。 (来自《深入理解Linux内核第三版》p47)


    虚拟内存与物理内存存在映射关系操作系统为每一个进程维护了一个从虚拟地址到物理地址的映射关系的数据结构,叫页表
    Linux页表结构是与平台无关的,并且设计成适用于提供对三级分页的硬件支持的64 位Alpha处理器。。页表中的每一项都记录了这个页的基地址。通过页表寻址完成虚拟地址和物理地址的转换。

    在这里插入图片描述在这里插入图片描述

    Linux使用三级页表结构,由下面几种类型的表组成( 每个表的大小都是一页):
    页目录 (PGD):一个活动进程有一个页目录,页目录为一页尺寸。页目录中的每一项指向页中间目录中的一顶。每个活动进程的页目录都必须在内存中。
    页中间目录 (PMD):页中间目录可能跨越多个页。页中间目录中的每一项指向页表中的一页。
    页表 (PTE):页表也可以跨越多个页。每个页表项指向该进程的一个虚拟页。

    为使用这个三级页表结构,Linux 中的虚拟地址被看做是由4个域组成,如图8.25所示。靠左也最重要的城作为页目录的索引,接下来的域作为页中间目录的索引,第三个域作为页表的索引,第四个域给出在内存中被选中页中的偏移量。

    在这里插入图片描述

    转换方法:

    逻辑地址虚拟地址(线性地址):段基址 + 段内偏移 = 线性地址

    虚拟地址物理地址: 页目录号 + 页表号 + 页内偏移,它们分别对应了32位线性地址10位 + 10位 + 12位
    在这里插入图片描述

    页表

    是什么?
    这些功能是由软硬件联合提供的,包括操作系统软件、MMU(内存管理单元)中的地
    址翻译硬件和一一个存放在物理内存中叫做页表(pagetable)的数据结构,页表将虚拟页映射到物理页。每次地址翻译硬件将-一个虚拟地址转换为物理地址时,都会读取页表。操作系统负责维护页表的内容,以及在磁盘与DRAM之间来回传送页。

    页表是虚拟内存的概念是操作系统虚拟内存到物理内存的映射表

    为什么存在页表

    原因:
    如果将每一个虚拟内存的 Byte 都对应到物理内存的地址,每个条目最少需要 8字节(32位虚拟地址->32位物理地址),在 4G 内存的情况下,就需要 32GB 的空间来存放对照表,那么这张表就大得真正的物理地址也放不下了,于是操作系统引入了页(Page)的概念。

    在系统启动时,操作系统将整个物理内存以 4K 为单位,划分为各个页。之后进行内存分配时,都以页为单位,那么虚拟内存页对应物理内存页的映射表就大大减小了,4G 内存,只需要 8M 的映射表即可,一些进程没有使用到的虚拟内存,也并不需要保存映射关系,而且Linux 还为大内存设计了多级页表,可以进一页减少了内存消耗。

    虚拟内存优缺点

    优点:
    (1)扩大地址空间。每个进程独占一个4G空间,虽然真实物理内存没那么多。
    (2)内存保护:防止不同进程对物理内存的争夺和践踏,可以对特定内存地址提供写保护,防止恶意篡改。
    (3)可以实现内存共享,方便进程通信。
    (4)可以避免内存碎片,虽然物理内存可能不连续,但映射到虚拟内存上可以连续。

    缺点:
    (1)虚拟内存需要额外构建数据结构,占用空间。
    (2)虚拟地址到物理地址的转换,增加了执行时间。
    (3)页面换入换出耗时。
    (4)一页如果只有一部分数据,浪费内存。




    参考书籍:
    《深入理解Linux内核第三版》
    《深入理解计算机系统第三版》
    《操作系统教程第二版》
    《LINUX内核设计与实现》

    展开全文
  • 它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。 对于 C 语言里面的变量,我们...
  • 当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存,获得的仅仅是对一个新的线性地址区间的使用权。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会...
  • 文章目录虚拟内存空间空户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc 虚拟内存空间 空户空间 内核空间 用户空间内存分配 malloc 内核空间内存分配 kmalloc vmalloc


    Linux 内存管理 | 物理内存管理:内存碎片、伙伴系统、slab分配器
    在上一篇博客中我介绍了Linux中对于物理内存的管理方式,这次再来介绍一下Linux对虚拟内存的管理方式

    虚拟内存空间

    即使是在现代,内存依旧是一项宝贵的资源,并且内存的管理以及访问控制向来都是难题。如果直接使用物理内存,通常都会面临以下几种问题

    • 内存缺乏访问控制,安全性不足
    • 各进程同时访问物理内存,可能会互相产生影响,没有独立性
    • 物理内存极小,而并发执行进程所需又大,容易导致内存不足
    • 进程所需空间不一,容易导致内存碎片化问题。

    基于以上几种原因,Linux通过mm_struct结构体来描述了一个虚拟的,连续的,独立的地址空间,也就是我们所说的虚拟地址空间

    在建立了虚拟地址空间后,并没有分配实际的物理内存,而当进程需要实际访问内存资源的时候就会由内核的请求分页机制产生缺页中断,这时才会建立虚拟地址和物理地址的映射,调入物理内存页。通过这种方法,就能够保证我们的物理内存只在实际使用时才进行分配,避免了内存浪费的问题。

    下图则为Linux下的虚拟地址空间
    在这里插入图片描述
    在Linux中,虚拟内存空间的内部又被划分为用户空间内核空间


    用户空间

    用户空间即进程在用户态下能够访问的虚拟地址空间,每个进程都有自己独立的用户空间,在32位系统下总容量为3G
    在这里插入图片描述
    用户空间由以下部分组成

    • :栈用来存放程序中临时创建的局部变量,如函数的参数、内部变量等。每当一个函数被调用时,就会将参数压入进程调用栈中,调用结束后返回值也会被放回栈中。同时,每调用一次函数就会创建一个新的栈,所以在递归较深时容易导致栈溢出。栈内存的申请和释放由编译器自动完成,并且栈容量由系统预先定义。栈从高地址向低地址增长
    • 文件映射段:也叫共享区,文件映射段中主要包括共享内存、动态链接库等共享资源,从低地址向高地址增长。
    • :堆用来存放动态分配的内存。堆内存由用户申请分配和释放,从低地址向高地址增长。
    • BSS段:BSS段用来存放程序中未初始化的全局变量和静态变量
    • 数据段:数据段用来存放程序中已初始化全局变量与静态变量
    • 代码段:代码段用来存放程序执行代码,也可能包含一些只读的常量。这块区域的大小在程序运行时就已经确定,并且为了防止代码和常量遭到修改,代码段被设置为只读。

    内核空间

    内核空间即进程陷入内核态后才能够访问的空间。虽然每个进程都具有自己独立的虚拟地址空间,但是这些虚拟地址空间中的内核空间 ,其实都关联的是同一块物理内存,如下图。
    在这里插入图片描述
    通过这种方法,保证了进程在切换至内核态后能够快速的访问内核空间。

    在32位系统中,内核空间的大小为1G,从0xC0000000到0xFFFFFFFF
    在这里插入图片描述

    如上图,内核空间主要分为直接映射区高端内存映射区两部分

    直接映射区

    从内核空间起始位置开始,从低地址往高地址增长,最大为896M的区域即为直接映射区。

    直接映射区的896M的虚拟地址与物理地址的前896M进行直接映射,所以虚拟地址和分配的物理地址都是连续的。

    那么它们是如何转换的呢?其实它们之间存在着一个偏移量PAGE_OFFSET,偏移量的大小即为0xC0000000

    所以虚拟地址 = PAGE_OFFSET + 物理地址

    高端内存映射区

    在上面也提到了,内核空间利用直接映射区来将896M的内存直接映射到物理内存中,但是我们的物理内存远远不止这么点,那么对于剩下的物理内存的寻址工作,就交给了高端内存映射区

    由于我们的内核空间只有1G,而直接映射区又占据了896M,因此我们将剩下的128M空间划分成了三个高端内存的映射区,从上往下分别是固定内存映射区,永久内存映射区,动态内存映射区

    • 动态内存映射区该区域的特点是虚拟地址连续,但是其对应的物理地址并不一定连续。该区域使用内核函数vmalloc进行分配,分配的虚拟地址的物理页可能会处于低端内存,也可能处于高端内存
    • 永久内存映射区该区域可以访问高端内存。使用alloc_page(_GFP_HIGHMEM)分配高端内存页,或者使用kmap将分配的高端内存映射到该区域
    • 固定内存映射区该区域的每个地址项都服务于特定的用途,如ACPI_BASE

    用户空间内存分配

    malloc

    了解C语言的同学都应该知道,在C语言中我们可以使用malloc来在用户空间中动态的分配内存,而malloc作为库函数,其本质就是对系统调用进行了一层封装,因此在不同的系统下其实现不同。

    在Linux中,当我们申请的内存小于128K时,malloc会使用sbrk或者brk区分配内存。而当我们申请大于128K的大块空间时,会使用mmap映射区进行分配。

    但是由于上述的brk/sbrk/mmap都属于系统调用,因此当我们每次调用它们时,就会从用户态切换至内核态,在内核态完成内存分配后再返回用户态。

    倘若每次申请内存都要因为系统调用而产生大量的CPU开销,那么性能会大打折扣。并且从上面的图我们也可以看出来,堆是从低地址往高地址增长,如果低地址的内存没有被释放,则高地址的内存就不能被回收,就会产生内存碎片的问题。

    malloc是如何实现解决这个问题的呢?

    为了减少内存碎片和系统调用的开销,malloc在底层采用了内存池来解决这个问题。

    它会先申请大块内存作为堆区,然后将这块内存拆分为多个不同大小的内存块,以作为内存管理的基本单位。同时,会使用隐式链表来连接所有的内存块,包括已分配块和未分配块。为了方便内存空闲块的管理,malloc采用显式链表来管理所有的空闲块

    当我们调用malloc进行内存分配时,就会去搜索空闲链表,找到满足需求的内存块,如果内存块过大,则会将内存块拆分为两部分,即一部分用来分配,另一部分则变为新的空闲块。

    同理,当我们释放内存块时,会通过遍历隐式链表,判断释放块前后内存块是否空闲,来决定是否需要合并内存块


    内核空间内存分配

    在内核空间中,通过与malloc类似的两个系统调用来进行内存的分配,它们 分别是kmallocvmalloc

    kmalloc

    kmalloc与上面介绍的用户空间的malloc函数非常类似,其用于为内核空间的直接内存映射区分配内存。

    kmalloc以字节为分配单位,通常用于分配小块内存,并且kmalloc确保分配的页在物理地址上是连续的(虚拟地址也必然连续)。并且kmalloc为了防止内存碎片的问题,其底层页面分配算法是基于slab分配器实现的。

    vmalloc

    vmalloc用于为内核空间中的动态内存映射区进行内存分配。

    vmalloc的工作方式与kmalloc类似,不同的地方在于vmalloc分配的内存只保证了虚拟地址是连续的,而物理地址不一定连续。它通过分配非连续的物理内存块,再通过修正页表的映射关系,把内存映射到虚拟地址空间的连续区域,就能够做到这一点。

    在这里插入图片描述
    如上图,就是内核空间中进行内存分配的具体流程

    展开全文
  • 进程地址空间的分布,虚拟内存物理内存的映射关系,页表
  • 进程虚拟地址空间 程序员在代码中看到的地址并不是物理内存地址,而是操作系统内核虚拟出来的地址(进程的虚拟地址空间),虚拟地址并不能保存数据,而真正保存数据的是物理内存 使用fork()创建一个子进程,放一个...
  • 详见链接https://www.cnblogs.com/wanghuaijun/p/7624564.html
  • Linux 虚拟内存物理内存

    千次阅读 2018-12-28 17:37:05
    操作系统为了屏蔽I/O底层的差异,创建了VFS(虚拟文件系统),为了屏蔽I/O层内存之间的差异,产生了虚拟内存。为了屏蔽cpu内存之间的差异,创建了进程。每个程序运行起来都会拥有一个自己的虚拟地址空间,32位cpu...
  • linux内核书籍中,介绍内存管理的部分,频繁出现三个概念,物理地址虚拟地址、总线地址 他们区别如下: 物理地址:MMU看到的内存地址 虚拟地址:cpu,程序员操作的地址 总线地址:设备看到的地址 ...
  • 项目中经常需要把内存数据dump出来看看是否自己设想的一样,dump... ARM小机端的内存起始地址并不是0,而是0x40000000也就是说虚拟地址与物理内存起始地址两者的差为:0x80000000那如果要将内核的虚拟地址转换为物理
  • linux虚拟内存物理内存 虚拟地址空间Linux整体架构图Linux虚拟内存系统内存管理分页式内存管理分段式内存管理段页式内存管理 虚拟地址空间 参考: ... 地址空间:非负整数地址的有序集合,如{0,1,2,...}\{0,1,2,...\...
  • linux服务器的物理内存虚拟内存

    千次阅读 2018-01-06 11:11:04
    [appdeploy@CNSZ22PL0217:/home/...分析:以上数据说明该服务器的总物理内存为252G,已使用的内存107G,剩余144G为使用,其中物理内存使用=used-buffers-cached=107-0-78=29G,还剩252-29=223G可以使用,剩余较多
  • documentlinux内存机制CPU内存虚拟内存硬盘物理内存内存和虚拟内存跟 Windows 完全不同的 Linux 内存机制Swap配置对性能的影响 linux内存机制 Linux支持虚拟内存(Virtual Mmemory),虚拟内存是指使用磁盘当作RAM的...
  • 详解linux虚拟内存原理

    千次阅读 2021-01-12 17:38:51
    linux内核相关视频解析: ...Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的,进程就可以很方便地访问内存,也就是我们常说的虚拟内存虚拟内存。 根据地址范围的不同,我们分为32位
  • Linux内存--虚拟内存

    千次阅读 2022-03-14 09:59:35
    虚拟内存是操作系统物理内存和进程之间的中间层,它为进程隐藏了物理内存这一概念,为进程提供了更加简洁和易用的接口以及更加复杂的功能。虚拟内存≠Swap分区。 如果虚拟内存=Swap分区,那么Linux的Swap分区不应该...
  • 之前在看malloc内存分配函数的原理时,有涉及到分配虚拟内存,然后再映射到物理内存,当初也是看得一头雾水,因为对虚拟内存物理内存不是很了解。所以这篇文章总结下我在学习虚拟内存物理内存的一些收获。 首先...
  • 内存杂谈 虚拟内存与物理内存 为什么要有虚拟内存??? 1.每个进程有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址(每个进程都假设自己...数据或代码页会根据需要在物理内存与磁盘之间移动。(如linux...
  • linux内核虚拟内存物理内存的映射

    千次阅读 2018-08-24 15:06:16
    NUMA指CPU对不同内存单元的访问时间可能不一样,因而这些物理内存被划分为几个节点,每个节点里的内存访问时间一致,NUMA体系结构主要存在大型机器、alpha等,嵌入式的基本都是UMA。UMA也使用了节点概念,只是永远都...
  • 虚拟地址与虚拟内存的理解

    千次阅读 2019-04-16 10:38:00
    在最开始看书的时候,我知道程序在运行时,不是全部加入内存当中的,当然,这个现在依旧正确,在有了虚拟地址空间这个概念以后,我一直认为是在硬盘中划分出一块虚拟内存出来,然后给每个程序4G的硬盘空间,作为这个...
  • Linux 虚拟地址物理地址转换

    千次阅读 2019-03-08 13:58:14
    Table of Contents 概念 地址转换过程 概念 虚拟地址和物理地址的概念 CPU通过地址来访问内存中的单元,地址虚拟地址和物理地址之分,如果CPU没有MMU...直接被内存芯片(以下称为物理内存,以便与虚拟内...
  • Linux 虚拟内存物理内存的管理

    千次阅读 2016-07-16 10:55:57
    Linux 虚拟内存物理内存的理解 关于Linux 虚拟内存物理内存的理解。 首先,让我们看下虚拟内存: 第一层理解 1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2. 一个新进程建立的时候...
  • 接下来,我们根据上述描述,给出获取虚拟地址对应的物理地址的代码 #include #include #include #include #include #include #define page_map_file "/proc/self/...
  • 内存由大量word或array组成,每个word或array都有之关联的地址。现在,CPU的工作是从基于内存的程序计数器中获取指令。现在,这些指令可能会导致加载或存储到特定的存储器地址地址绑定是从一个地址空间映射到另...
  • Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该空间是块大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能...
  • Linux中的虚拟地址物理地址内存管理方式: http://m.blog.csdn.net/blog/yinjingyu_bisheng/8943650  Linux内存管理之mmap详解 : ...Linux驱动虚拟地址物理地址

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 145,606
精华内容 58,242
关键字:

linux 虚拟内存地址与物理内存地址