精华内容
下载资源
问答
  • 虚拟地址空间

    千次阅读 多人点赞 2019-03-13 03:32:18
    对于每一个进程都会对应一个虚拟地址空间,对于32位的操作系统(其指令的位数最大为32位,因此地址码最多32位),虚拟地址空间的大小为B即0~4GB的虚拟地址空间,其中内核空间为1GB,如下所示: 每一个进程的进程...

            对于每一个进程都会对应一个虚拟地址空间,对于32位的操作系统(其指令的位数最大为32位,因此地址码最多32位),虚拟地址空间的大小为2^{32}B即0~4GB的虚拟地址空间,其中内核空间为1GB,如下所示:

             每一个进程的进程控制块PCB都位于内核区,在每一个进程的PCB中有一个文件描述符表(是一个数组),用于标记该进程所打开的所有文件。从文件描述符表可以看出每一个进程最多能打开1024个文件,其中有三个文件默认是一直处于打开状态的(即进程创建完成时就处于打开状态),分别是:标准输入 STDIN_FILENO,其文件描述符为0;标准输出 STDOUT_FILENO,其文件描述符为1;错误输出 STDERR_FILENO,其文件描述符为2,其中文件描述符0和1可以省略不写。供我们用户打开的文件,只能够占据从3开始的位置(即其文件描述符为3以后的数字,3~1023)。每打开一个文件就会占用一个文件描述符,且使用的是空闲的最小的一个文件描述符。

            Linux下可执行文件的格式为ELF:[root@localhost Calc]# file zsx
                                                                         zsx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x14ef2d34126e7c54141b73c31968bd825ca522ba, not stripped           //可以看出zsx为64位(即机器指令位数为64位,OS位数)的可执行文件,其格式为ELF。

            对于每一个程序在执行时(如上图中的a.out),此时会产生一个相应的进程,系统都会自动为其分配一个0~4G的虚拟地址空间,其中1G的内核空间用于:进程管理、内存管理、设备管理和虚拟文件系统等。下面详细介绍0~3G的用户空间。

             强调一点:以下说明的各段都是与编程相关的,不包括虚拟地址空间的全部。

            0~3G的用户空间。从小到大(从下往上)依次为:保留区(受保护的地址)、代码段、数据段(.data段)、.bss段、堆空间、内存映射段、栈空间、命令行参数和环境变量。下面依次对每一个段做简单的介绍:

    1.保留区(受保护的地址)

            保留区即为受保护的地址,大小为0~4K,位于虚拟地址空间的最低部分,未赋予物理地址(不会与内存地址相对应,因此其不会放任何内容)。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。将指针赋值为0,意味着该指针将永远不会被使用,从而不会出现野指针情况。#define NULL 0 与 #define NULL (void*)0   在C语言中是等效的,而在C++中,只能用#define NULL 0,后面 #define NULL (void*)0的使用会出错。

    2.代码段

            代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。  

    3.数据段(.data段)

            数据段通常用于存放程序中已初始化的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。由于全局变量未初始化时,其默认值为0,因此值为0的全局变量位于.bbs段(不位于数据段)。对于未初始化的局部变量,其值是不可预测的。注意:在代码段和数据段之间还包括其它段:只读数据段和符号段等。

    4..bbs段

            该段用于存放未初始化的全局变量和静态局部变量,包括值为0的全局变量。 数据段和.bbs段又称为全局数据区,前者初始化,后者未初始化。

            ELF段包括:代码段、其它段(只读数据段和符号段等)、.data段(数据段)和.bbs段,都属于可执行程序部分。

    5.堆空间

            new( )和malloc( )函数分配的空间就属于对空间,用于内存空间的分配,其从下往上。  堆用于存放进程运行时动态分配的内存段,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。当进程调用malloc(C) 和new (C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减) 。

    6.内存映射段(共享库)

            此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序都可通过Linux的mmap()系统调用请求这种映射。内存映射是一种方便高效的文件I/O方式, 因而被用于装载动态共享库。如C标准库函数(fread、fwrite、fopen等)和Linux系统I/O函数,它们都是动态库函数,其中C标准库函数都被封装在了/lib/libc.so库文件中,都是二进制文件。这些动态库函数都是与位置无关的代码,即每次被加载进入内存映射区时的位置都是不一样的,因此使用的是其本身的逻辑地址,经过变换成线性地址(虚拟地址),然后再映射到内存。而静态库不一样,由于静态库被链接到可执行文件中,因此其位于代码段,每次在地址空间中的位置都是固定的。

    7.栈空间

            用于存放局部变量(非静态局部变量,C语言称为自动变量),分配存储空间时从上往下。栈和堆都是后进先出的数据结构。

    8.命令行参数

            该段用于存放命令行参数的内容:argc和argv。

    9.环境变量

            用于存放当前的环境变量,在Linux中用env命令可以查看其值。

    10.虚拟地址空间的作用(好处)

            1.方面编译器和操作系统安排程序的地址;2.方便实现各个进程空间之间的隔离,互不干扰,因为每个进程都对应自己的虚拟地址空间;3.实现虚拟存储,从逻辑上扩大了内存。

    补充内容:

    代码段(.text段)与只读数据段和符号段(.rodata段),都属于只能读的部分,在链接的时候这两部分会链接成为一个整体;而.data段和.bbs段属于可读可写RW的部分。这四个部分都是以页(每页4KB)的形式存放在内存中。进程控制块PCB(又叫进程描述符)放于内核空间

    多个进程在并发执行时,这些进程的用户空间都是彼此独立的,因此各个进程的用户空间在映射为内存空间使都是独立的,互不干扰,这是MMU地址变换必须要能够保证的。例如,各个进程的.text段、只读数据段和符号段、.data段和.bbs段等在用户空间中使用到的其它数据信息,都会与页为基本单位放在内存中,各个进程的映射是独立的。而对于内核空间,由于只有一个操作系统,内核空间主要是 机器指令、操作系统内核的各个模块等,它们是公用的,因此每个进程的映射方式一样。强调一点:每个进程用到或即将用到的数据才会调入内存,其余都在磁盘上。但是各个进程内核空间的进程控制块(进程描述符)映射的地点是不一样的,也是相互独立的。共用的模块才是一样的。 这些都是MMU的实现机制所决定的。如果感兴趣,可以看看MMU的实现机制。

    展开全文
  • 虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含...
  • Linux虚拟地址空间

    千次阅读 2018-11-02 21:18:47
    Linux虚拟地址空间 注:本文来自多篇博客整理,具体博客链接在博客下方 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的...

    Linux虚拟地址空间

    注:本文来自多篇博客整理,具体博客链接在博客下方

    在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块,这篇博客均是X86架构的

    1. 地址空间分布

    在这里插入图片描述

    2. 内核地址空间

    其中高1G为内核空间,所有进程共享内核地址空间,内核空间分三部分:DMA映射区,一致映射区、高端内存区一致映射区的虚拟地址均一一对应了物理页框,因此此区间虚拟地址的访问可以直接通过偏移量得到物理内存而不需进行页表的转换,其中的高128M空间为高端内存,当物理内存大于4G时内核用128M的地址空间作为高端内存,扮演着临时映射的作用。我的能力现在有限主要解释用户地址空间,以后阅读内核代码会对内核空间进行解释。
    Temporary Kernel Mapping 为固定映射空间
    在这个空间中,有一部分用于高端内存的临时映射。当要进行一次临时映射的时候,需要指定映射的目的,根据映射目的,可以找到对应的小空间,然后把这个空间的地址作为映射地址。
    Persistent Kernel Mapping 永久映射空间
    从PKMAP_BASE 到 FIXADDR_START用于映射高端内存
    Vmalloc Area loremap Area 动态映射空间

    3. 用户地址空间

    Envioment Variables 为环境变量
    Command line arguments 为命令行参数
    Random stack offset和Random mmap offset等随机值意在防止恶意程序。Linux通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱布局,以免恶意程序通过计算访问栈、库函数等

    (1). 栈

    栈可以被称为堆栈,由编译器进行管理(自动释放和分配)并且是先入后出,这里说的堆栈是进程栈(栈分为进程栈,内核栈,线程栈, 中断栈)
    进程栈的初始化大小是由编译器和链接器计算出来的,但是栈的实时大小并不是固定的,Linux 内核会根据入栈情况对栈区进行动态增长(其实也就是添加新的页表)。但是并不是说栈区可以无限增长,它也有最大限制 RLIMIT_STACK (一般为 8M)。
    栈的特点和用途:
    1. 进程调用函数产生的栈帧建立在栈区(首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行下条可执行语句)
    2. 临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存,或者c++的临时对象。
    3. 栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行, 其相比堆效率较高

    进程栈的动态增长实现
    进程在运行的过程中,通过不断向栈区压入数据,当超出栈区容量时,就会耗尽栈所对应的内存区域,这将触发一个 缺页异常 (page fault)。通过异常陷入内核态后,异常会被内核的 expand_stack() 函数处理,进而调用 acct_stack_growth() 来检查是否还有合适的地方用于栈的增长。
    如果栈的大小低于 RLIMIT_STACK(通常为8MB),那么一般情况下栈会被加长,程序继续执行,感觉不到发生了什么事情,这是一种将栈扩展到所需大小的常规机制。然而,如果达到了最大栈空间的大小,就会发生 栈溢出(stack overflow),进程将会收到内核发出的 段错误(segmentation fault) 信号。
    动态栈增长是唯一一种访问未映射内存区域而被允许的情形,其他任何对未映射内存区域的访问都会触发页错误,从而导致段错误。一些被映射的区域是只读的,因此企图写这些区域也会导致段错误。

    (2). 内存映射段(mmap)

    内存映射是一种方便高效的文件I/O方式(内核直接将硬盘文件映射到虚拟内存中), 因而被用于装载动态共享库, 同时可以用于映射可执行文件用到的动态链接库。
    在内存映射段, 进程可以不使用read() ,write()等函数对文件操作,像访问普通内存对文件进行访问,可通过Linux的mmap()系统调用函数将一个普通文件映射到此段,然后进行操作,另外mmap并不分配空间, 只是将文件映射到调用进程的地址空间里(但是会占掉虚拟内存地址)可以通过munmap()来取消内存映射。
    注意:在 Linux中,若通过malloc()请求一大块内存,C运行库将创建一个匿名内存映射,而不使用堆内存。”大块” 意味着比阈值 MMAP_THRESHOLD还大,缺省为128KB,可通过mallopt()调整
    另外线程栈是也是在内存映射段
    因为线程和进程共享同一地址空间,如果线程和进行共用同一栈会造成调用栈混乱,因此在线程创建的时候使用mmap 系统调用为线程在内存映射段映射一块固定大小空间作为线程栈,因此线程栈不能动态增长。

    (3). 堆

    堆用于存放进程运行时动态分配的空间,可动态扩张或者缩减,堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。当进程调用malloc©/new(C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free©/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减)

    堆的特点:
    1> 堆管理器通过链表管理堆,由于申请和释放堆内存是无序的因此会产生内存碎片,堆的释放由程序员完成,回收的内存可以重新使用, 如果不释放只有在程序结束时才会统一释放。
    2> 堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆(向上生长并且32位Linux系统中堆内存理论上可达2.9G空间),一般由系统自动调用。
    3> 操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间。此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空闲链表中。

    使用堆时经常出现两种问题
    1> 释放或改写仍在使用的内存(“内存破坏”);
    2> 未释放不再使用的内存(“内存泄漏”)。当释放次数少于申请次数时,可能已造成内存泄漏。泄漏的内存往往比忘记释放的数据结构更大,因为所分配的内存通常会圆整为下个大于申请数量的2的幂次(如申请212B,会圆整为256B)。

    (4). BSS段

    BSS段用于存放程序的以下符号:

    • 程序中的未初始化的全局变量和静态局部变量
    • 初始值为0的全局变量和静态局部变量(依赖于编译器实现)
    • 未定义且初值不为0的符号

    由于程序加载时,BSS会被操作系统清零,所以未赋初值或初值为0的全局变量都在BSS中。BSS段仅为未初始化的静态分配变量预留位置,在目标文件中并不占据空间,这样可减少目标文件体积。但程序运行时需为变量分配内存空间,故目标文件必须记录所有未初始化的静态分配变量大小总和(通过start_bss和end_bss地址写入机器代码)。当加载器(loader)加载程序时,将为BSS段分配的内存初始化为0

    尽管均放置于BSS段,但初值为0的全局变量是强符号,而未初始化的全局变量是弱符号。若其他地方已定义同名的强符号(初值可能非0),则弱符号与之链接时不会引起重定义错误,但运行时的初值可能并非期望值(会被强符号覆盖)。因此,定义全局变量时,若只有本文件使用,则尽量使用static关键字修饰;否则需要为全局变量定义赋初值(哪怕0值),保证该变量为强符号,以便链接时发现变量名冲突,而不是被未知值覆盖。
    gcc将未初始化的全局变量保存在common段,链接时再将其放入BSS段。在编译阶段可通过-fno-common选项来禁止将未初始化的全局变量放入common段。

    (5). 数据段

    数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。数据段保存在目标文件中,其内容由程序初始化。例如,对于全局变量int gVar = 10,必须在目标文件数据段中保存10这个数据,然后在程序加载时复制到相应的内存。

    数据段与BSS段的区别如下:
    1> BSS段不占用物理文件尺寸,但占用内存空间;数据段占用物理文件,也占用内存空间。
    对于大型数组如int ar0[1000] = {1, 2, 3, …}和int ar1[1000],ar1放在BSS段,只记录共有1000*4个字节需要初始化为0,而不是像ar0那样记录每个数据1、2、3…,此时BSS为目标文件所节省的磁盘空间相当可观。
    2) 当程序读取数据段的数据时,系统会出发缺页故障,从而分配相应的物理内存;当程序读取BSS段的数据时,内核会将其转到一个全零页面,不会发生缺页故障,也不会为其分配相应的物理内存。

    (6). 代码段

    代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。
    代码段还存放一些只读数据如字符串常量。

    代码段指令中包括操作码和操作对象(或对象地址引用)。若操作对象是立即数(具体数值),将直接包含在代码中;若是局部数据,将在栈区分配空间,然后引用该数据地址;若位于BSS段和数据段,同样引用该数据地址

    (7) 保留区

    位于虚拟地址空间的最低部分,未赋予物理地址。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。
    它并不是一个单一的内存区域,而是对地址空间中受到操作系统保护而禁止用户进程访问的地址区域的总称。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。

    在32位X86架构的Linux系统中,用户进程可执行程序一般从虚拟地址空间0x08048000开始加载。该加载地址由ELF文件头决定,可通过自定义链接器脚本覆盖链接器默认配置,进而修改加载地址。0x08048000以下的地址空间通常由C动态链接库、动态加载器ld.so和内核VDSO(内核提供的虚拟共享库)等占用。通过使用mmap系统调用,可访问0x08048000以下的地址空间。

    4. 内存描述符对虚拟地址空间的定义

    struct mm_struct {
        struct vm_area_struct *mmap;           /* 内存区域链表 */
        struct rb_root mm_rb;                  /* VMA 形成的红黑树 */
        ...
        struct list_head mmlist;               /* 所有 mm_struct 形成的链表 */
        ...
        unsigned long total_vm;                /* 全部页面数目 */
        unsigned long locked_vm;               /* 上锁的页面数据 */
        unsigned long pinned_vm;               /* Refcount permanently increased */
        unsigned long shared_vm;               /* 共享页面数目 Shared pages (files) */
        unsigned long exec_vm;                 /* 可执行页面数目 VM_EXEC & ~VM_WRITE */
        unsigned long stack_vm;                /* 栈区页面数目 VM_GROWSUP/DOWN */
        unsigned long def_flags;
        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;        /* 命令行参数 和 环境变量的 起始地址和结束地址 */
        ...
        /* Architecture-specific MM context */
        mm_context_t context;                  /* 体系结构特殊数据 */
     
        /* Must use atomic bitops to access the bits */
        unsigned long flags;                   /* 状态标志位 */
        ...
        /* Coredumping and NUMA and HugePage 相关结构体 */
    };
    

    在这里插入图片描述

    来源博客
    Linux虚拟地址空间布局
    Linux用户空间与内核地址空间
    linux内存映射mmap原理分析

    展开全文
  • 虚拟地址空间映射到物理地址空间

    千次阅读 2018-09-07 09:06:45
    虚拟地址空间映射到物理地址空间参考如下  当处理器读或写入内存位置时,它会使用虚拟地址。作为读或写操作的一部分,处理器将虚拟地址转换为物理地址。通过虚拟地址访问内存有以下优势:  程序可以使用一系列...

    虚拟地址空间映射到物理地址空间参考如下

      当处理器读或写入内存位置时,它会使用虚拟地址。作为读或写操作的一部分,处理器将虚拟地址转换为物理地址。通过虚拟地址访问内存有以下优势:

      程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。

      程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

      不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。

      进程可用的虚拟地址范围称为该进程的“虚拟地址空间”。每个用户模式进程都有其各自的专用虚拟地址空间。 对于 32 位进程,虚拟地址空间通常为 2 GB,范围从 0x00000000 至 0x7FFFFFFF。对于 64 位进程,虚拟地址空间为 8 TB,范围从 0x000'00000000 至 0x7FF'FFFFFFFF。一系列虚拟地址有时称为一系列“虚拟内存”。

      此图说明了虚拟地址空间的一些重要功能。

                                                                      èæå°å空é´æ å°å°ç©çå°å空é´

     

    该图显示了两个 64 位进程的虚拟地址空间:Notepad.exe 和 MyApp.exe。每个进程都有其各自的虚拟地址空间,范围从 0x000'0000000 至 0x7FF'FFFFFFFF。每个阴影框都表示虚拟内存或物理内存的一个页面(大小为 4 KB)。注意,Notepad 进程使用从 0x7F7'93950000 开始的虚拟地址的三个相邻页面。但虚拟地址的这三个相邻页面会映射到物理内存中的非相邻页面。而且还注意,两个进程都使用从 0x7F7'93950000 开始的虚拟内存页面,但这些虚拟页面都映射到物理内存的不同页面。

      用户空间和系统空间

      诸如 Notepad.exe 和 MyApp.exe 的进程在用户模式下运行。核心操作系统组件和多个驱动程序在更有特权的内核模式下运行。有关处理器模式的详细信息,请参阅用户模式和内核模式。每个用户模式进程都有其各自的专用虚拟地址空间,但在内核模式下运行的所有代码都共享称为“系统空间”的单个虚拟地址空间。当前用户模式进程的虚拟地址空间称为“用户空间”。

      在 32 位 Windows 中,可用的虚拟地址空间共计为 2^32 字节(4 GB)。通常较下的 2 GB 用于用户空间,较上的 2 GB 用于系统空间。

                                                            èæå°å空é´æ å°å°ç©çå°å空é´

     

    在 32 位 Windows 中,你可以指定(在启动时)超过 2 GB 用于用户空间。结果是系统空间可用的虚拟地址更少。可以将用户空间的大小增至 3 GB,在这种情形下系统空间仅有 1 GB。若要增大用户空间的大小,请使用 BCDEdit /set increaseuserva。

      在 64 位 Windows 中,虚拟地址空间的理论大小为 2^64 字节(16 百亿亿字节),但实际上仅使用 16 百亿亿字节范围的一小部分。范围从 0x000'00000000 至 0x7FF'FFFFFFFF 的 8 TB 用于用户空间,范围从 0xFFFF0800'00000000 至 0xFFFFFFFF'FFFFFFFF 的 248 TB 的部分用于系统空间。

                                                                                              èæå°å空é´æ å°å°ç©çå°å空é´

    用户模式下运行的代码可以访问用户空间,但不能访问系统空间。此限制可防止用户模式代码读或更改受保护的操作系统数据结构。内核模式下运行的代码既可以访问用户空间,也可以访问系统空间。即,在内核模式下运行的代码可以访问系统空间和当前用户模式进程的虚拟地址空间。

      在内核模式下运行的驱动程序必须在直接从用户空间地址中读取或写入这些地址时非常小心。此方案说明了原因。

      用户模式程序发起从设备读取某些数据的请求。程序提供缓冲区的起始地址以接收数据。

      在内核模式下运行的设备驱动程序例程启动读取操作并将控制权返回到其调用程序。

      然后,设备中断了当前运行的任何线程以显示读取操作完成。 中断由在此任意线程上运行的内核模式驱动程序例程进行处理,该例程属于任意进程。

      此时,驱动程序不得将数据写入用户模式程序在步骤 1 中提供的开始地址。此地址位于发起请求的进程的虚拟地址空间,该进程可能很大程度上不同于当前进程。

      虚拟地址(Virtual Address Space)

      Win32通过一个两层的表结构来实现地址映射,因为每个进程都拥有私有的4G的虚拟内存空间,相应的,每个进程都有自己的层次表结构来实现其地址映射。

      第一层称为页目录,实际就是一个内存页,Win32的内存页有4KB大小,这个内存页以4个字节分为1024项,每一项称为“页目录项”(PDE);

      第二层称为页表,这一层共有1024个页表,页表结构与页目录相似,每个页表也都是一个内存页,这个内存页以4KB的大小被分为1024项,页表的每一项被称为页表项(PTE),易知共有1024×1024个页表项。每一个页表项对应一个物理内存中的某一个“内存页”,即共有1024×1024个物理内存页,每个物理内存页为4KB,这样就可以索引到4G大小的虚拟物理内存。

      如下图所示(注下图中的页目录项的大小应该是4个字节,而不是4kB):

                                      èæå°å空é´æ å°å°ç©çå°å空é´

    Win32提供了4GB大小的虚拟地址空间。因此每个虚拟地址都是一个32位的整数值,也就是我们平时所说的指针,即指针的大小为4B。它由三部分组成,如下图: 

                                      

    这三个部分的第一部分,即前10位为页目录下标,用来寻址页目录项,页目录项刚好1024个。找到页目录项后,找对页目录项对应的的页表。第二部分则是用来在页表内寻址,用来找到页表项,共有1024个页表项,通过页表项找到物理内存页。第三部分用来在物理内存页中找到对应的字节,一个页的大小是4KB,12位刚好可以满足寻址要求。

      具体的例子:

      假设一个线程正在访问一个指针(Win32的指针指的就是虚拟地址)指向的数据,此指针指为0x2A8E317F,下图表示了这一个过程:

                                                 

     

    0x2A8E317F的二进制写法为0010101010_0011100011_000101111111,为了方便我们把它分为三个部分。

      首先按照0010101010寻址,找到页目录项。因为一个页目录项为4KB,那么先将0010101010左移两位,001010101000(0x2A8),用此下标找到页目录项,然后根据此页目录项定位到下一层的某个页表。

      然后按照0011100011寻址,在上一步找到页表中寻找页表项。寻址方法与上述方法类似。找到页表项后,就可以找到对应的物理内存页。

      最后按照000101111111寻址,寻找页内偏移。

      上面的假设的是此数据已在物理内存中,其实判断访问的数据是否在内存中也是在地址映射过程中完成的。Win32系统总是假设数据已在物理内存中,并进行地址映射。页表项中有一位标志位,用来标识包含此数据的页是否在物理内存中,如果在的话,就直接做地址映射,否则,抛出缺页中断,此时页表项也可标识包含此数据的页是否在调页文件中(外存),如果不在则访问违例,程序将会退出,如果在,页表项会查出此数据页在哪个调页文件中,然后将此数据页调入物理内存,再继续进行地址映射。为了实现每个进程拥有私有4G的虚拟地址空间,也就是说每个进程都拥有自己的页目录和页表结构,对不同进程而言,即使是相同的指针(虚拟地址)被不同的进程映射到的物理地址也是不同的,这也意味着在进程之间传递指针是没有意义的。

      看过“虚拟地址空间映射到物理地址空间 ”的人还看了:

    1.物理地址与虚拟地址映射

    2.linux虚拟地址怎么映射物理地址

    3.物理地址和虚拟地址

    4.物理地址与虚拟地址怎么转换

    5.Linux关于虚拟内存

     

    展开全文
  • 当一个程序加载到内存,操作系统会给他分配自己的虚拟地址空间虚拟地址空间主要分为两部分,分别是内核空间和用户空间。在Linux下内核空间一般是1G大小,用户空间4G大小。内核空间是所有的进程共享,随时准备处理...

    当一个程序加载到内存,操作系统会给他分配自己的虚拟地址空间。虚拟地址空间主要分为两部分,分别是内核空间和用户空间。在Linux下内核空间一般是1G大小,用户空间4G大小。内核空间是所有的进程共享,随时准备处理系统调用和异常的操作。用户空间是每一个进程独立的部分,同时保证了虚拟地址空间的独立性,通过mm_struct结构体进行描述,用户空间下的每个段都是通过vm_area_struct结构体描述。一个CPU统一时刻只能处理一个虚拟地址空间并且进行调度切换。

    在Linux下面查看一个进程的虚拟地址空间信息通过:cat proc/pid/maps

    用户空间:地址从大到小分为栈、共享区、堆、未初始化的数据、初始化的数据、代码段。栈主要存储函数参数、返回值和函数内部非静态局部变量等。堆内存在C语言中通过malloc等申请的内存空间,C++中是new申请的内存空间,并且堆内存需要用户自己手动释放。共享区可以装载一个共享的动态库,通过系统调用创建共享内存实现进程间通信。数据段分为以初始化和未初始化,主要存储全局变量和静态变量。代码段主要存储字符串常量,这个区域只具有可读性。

    上述我们提到了虚拟地址空间和程序地址空间。那么这两者之间有什么关系?

    程序地址空间就是虚拟地址空间,两者都不是真正的地址空间。真正的地址空间是物理内存。那为什么会有现代技术的虚拟地址空间呢?虚拟地址空间的出现主要下面原因:

    操作系统管理进程地址空间,其实管理的是虚拟地址空间,我们所写的代码打印出来的地址都是虚拟地址,物理地址是不允许我们访问的。那么为什么是虚拟地址空间,这就涉及到了早期地址空间布局。

    早期内存管理机制

    早期运行一个程序,会把这个程序全部装载到内存,所以计算机想要运行一个程序,必须要有足够的内存。如果内存大小100M,可执行程序A大小为25M,可执行程序B大小为50M,可执行程序C大小为30M,现在内存中运行程序A和B,如果C想要运行,必须要换出某个程序,导致数据在内存和磁盘之间来回拷贝,效率太慢。

    综上所述,可以看出早期的内存管理机制问题:

    1. 进程地址空间不隔离。每一个程序都是不独立运行在内存中,所以如果有恶意程序,可能会导致程序出现问题。
    2. 内存使用效率太低。如果某一个程序想要运行,可能会需要换出某个程序,导致数据的来回拷贝。其实与连续分配有关
    3. 程序运行的地址不确定。由于程序运行内存是随机分配的,所以程序的地址不确定。

    改进

    现代操作系统是通过分段和分页来进行内存管理。

    分段:cpu通过段寄存器将内存分为不同的段,所以指令和数据的有效地址并不是真正的物理地址,而是首地址加偏移地址。(1)因为段寄存器的存在,使得每个程序的地址空间隔离,所以越界问题,很容易判断出来。(2)实际代码和数据的地址都是偏移量,所以也得以确定。

    但是分段并没有解决内存使用效率问题。

    分页:分页管理主要针对物理内存和虚拟地址空间内存管理和映射机制。通过分页,进程实际所得到的物理内存是不连续的。但是通过虚拟内存机制,被看成是连续分配的了。

    虚拟内存和物理内存之间通过页表(MMU)来进行映射,所以可以看出页表主要作用是解决映射,除此之外还有保证内存的访问权限,如字符串存储在代码段,只具有可读性。

    综上,虚拟内存:

    1. 给所有进程提供一致的地址空间,每个进程都认为自己是在独占使用单机系统的存储资源
    2. 保护每个进程的地址空间不被其他进程破坏,隔离了进程的地址访问
    3. 分页管理主要针对物理内存和虚拟地址空间内存管理和映射机制。通过分页,进程实际所得到的物理内存是不连续的。但是通过虚拟内存机制,被看成是连续分配的了,提高了内存的利用率。
    展开全文
  • 进程虚拟地址空间

    千次阅读 2018-11-06 23:19:45
    进程虚拟地址空间的引入  1.程序与进程的区别 程序: 静态 预先编译好的指令和数据的集合 的一个文件 #菜谱 进程:动态 程序运行的过程 #炒菜的过程  2.虚拟地址空间: 程序运行后拥有自己独立的虚拟空间 大小...
  • 虚拟内存/虚拟地址空间

    千次阅读 2019-04-29 11:01:02
    虚拟内存: 也被称作“页面文件”,是一种逻辑上扩充物理内存的技术。...虚拟地址空间:32位CPU的地址总线的是32位的,也就是说可以寻找到2^32(4G)的地址空间。我们的程序被CPU执行,就是在0x000...
  • 1.虚拟内存是内存管理的一种方式, 它在磁盘上划分出一块空间由操作系统管理,当物理内存耗尽是充当物理内存来使用。...2.虚拟地址空间:在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中,这...
  • Linux虚拟地址空间分布

    千次阅读 2017-06-16 23:35:40
    Linux虚拟地址空间分布
  • 进程的虚拟地址空间

    千次阅读 2016-11-27 22:02:40
    进程的虚拟地址空间
  • Windows虚拟地址空间

    千次阅读 2017-04-02 12:28:28
    整理关于WIndows虚拟地址空间的一些知识。如果有什么不对,希望有人可以提出...
  • 虚拟地址空间 及 页表 详解

    千次阅读 2019-05-06 13:24:03
    虚拟地址空间 进程地址空间由进程可寻址的虚拟内存组成,内核允许进程使用这种虚拟内存的地址。每个进程都有一个 32位或64位 的平坦地址空间,空间的大小取决于体系结构。(平坦指的是地址空间范围是一个独立的连续...
  • 4G虚拟地址空间布局

    千次阅读 2018-11-14 03:32:21
    4G虚拟地址空间布局 4G的虚拟内存空间: 其中1G是属于内核空间,另外的3G属于用户空间 所有的进程都拥有属于自己的用户空间,但却共享一个内核空间 现在我们从上向下开始分析 首先是用户空间: ①:...
  • Linux——环境变量、虚拟地址空间

    千次阅读 2021-03-21 17:35:14
    文章目录一、环境变量1.1 常见的环境变量1.2 与环境变量相关的命令1.3 环境变量的组织方式二、虚拟地址空间2.2 虚拟地址空间的访问过程三、虚拟地址空间的管理方式3.1 页式管理3.2 段式管理3.3 段页式管理 ...
  • Linux虚拟地址空间概述

    千次阅读 2015-11-10 00:12:43
     Linux进程虚拟地址空间是linux内存管理一个重要的部分,我们知道,在IA-32系统上地址空间的范围可达2的32次幂=4G,总的地址空间通常按3:1的比例划分,用户态占用了3G,内核占用了1G。 各进程的用户态虚拟地址...
  • 进程虚拟地址空间的区域划分

    千次阅读 2019-11-24 18:42:40
    虚拟地址空间的区域划分及每块区域的作用
  • 虚拟地址空间以及编译模式

    千次阅读 2019-01-01 13:25:16
    所谓虚拟地址空间,就是程序可以使用的虚拟地址的有效范围。虚拟地址和物理地址的映射关系由操作系统决定,相应地,虚拟地址空间的大小也由操作系统决定,但还会受到编译模式的影响。 这节我们先讲解CPU,再讲解编译...
  • 》,我们知道了CPU是如何访问内存的,本篇文章我们来讲下虚拟地址空间和物理地址空间的映射。 通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,64位内核地址空间...
  • Windows虚拟地址空间分配问题

    千次阅读 2016-10-03 23:26:54
    一般而言,虚拟地址空间分为两个区,即为用户空间和系统空间。虚拟地址低空间,即从0x00000000~0X7FFFFFFF的2GB为用户空间,而高地址0x80000000~0xFFFFFFFF被分配给了系统内核。高地址空间2GB内存是提供系统内核使用...
  • 物理地址空间和虚拟地址空间

    千次阅读 2018-02-18 21:43:18
    因为CPU是32位的,其地址总线是32位的,所以其地址总线可编码的个数是2^32(4G),这2^32个物理地址的集合就是物理地址空间。这与38译码器的道理是一样的,这一点很好理解。 二、虚拟地址(空间)   &...
  • Linux虚拟地址空间布局以及进程栈和线程栈总结

    万次阅读 多人点赞 2016-12-21 18:55:46
    一:Linux虚拟地址空间布局 (转自:Linux虚拟地址空间布局)  在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存...
  • 虚拟地址空间的理解

    千次阅读 2014-06-14 22:58:31
    1、创建一个进程时,操作系统会为该进程分配一个 4GB 大小的虚拟 进程地址空间。之所以是 4GB ,是因为在 32 位...2、每个进程只能访问自己虚拟地址空间中的数据,无法访问别的进程中的数据,通过这种方法实现了进程间
  • 在早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要...
  • Linux 内核物理地址空间与虚拟地址空间布局(IA-32体系结构) 1. 杂言: 最近比较忙,故先放出一个自己总结的图。详细的解说分析待有时间时会给出。有些基础的人,看此图即可理解。 2. 正言: 在学习linux内核...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 302,033
精华内容 120,813
关键字:

虚拟地址空间