windows虚拟内存管理

内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理。在应用程序中我们无时不刻不在和内存打交道,我们总在不经意间的进行堆内存和栈内存的分配释放,所以内存是我们进行程序设计必不可少的部分。

CPU的内存管理方式

段寄存器怎么消失了?

在学习8086汇编语言时经常与寄存器打交道,其中8086CPU采用的内存管理方式为分段管理的方式,寻址时采用:短地址 * 16 + 偏移地址的方式,其中有几大段寄存器比如:CS、DS、SS、ES等等,每个段的偏移地址最大为64K,这样总共能寻址到2M的内存。但是到32位CPU之后偏移地址变成了32位这样每个段就可以有4GB的内存空间,这个空间已经足够大了,这个时候在编写相应的汇编程序时我们发现没有段寄存器的身影了,是不是在32位中已经没有段寄存器了呢,答案是否定了,32位CPU中不仅有段寄存器而且它们的作用比以前更大了。

在32位CPU中段寄存器不再作为段首地址,而是作为段选择子,CPU为了管理内存,将某些连续的地址内存作为一页,利用一个数据结构来说明这页的属性,比如是否可读写,大小,起始地址等等,这个数据结构叫做段描述符,而多个段描述符则组成了一个段描述符表,而段寄存器如今是用来找到对应的段描述符的,叫做段选择子。段寄存器仍然是16位其中高13位表示段描述符表的索引,第二位是区分LDT(局部描述符表)和GDT(全局描述符表),全局描述符表是系统级的而LDT是每个进程所独有的,如果第二位表示的是LDT,那么首先要从GDT中查询到LDT所在位置,然后才根据索引找到对应的内存地址,所以现在寻址采用的是通过段选择子查表的方式得到一个32位的内存地址。由于这些表都是由系统维护,并且不允许用户访问及修改所以在普通应用程序中没有必要也不能使用段寄存器。通过上面的说明,我们可以推导出来32位机器最多可以支持2^(13 + 1 + 32) = 64T内存。

段页式管理

通过查表方式得到的32位内存地址是否就是真实的物理内存的地址呢,这个也是不一定的,这个还要看系统是否开启了段页式管理。如果没有则这个就是真实的物理地址,如果开启了段页式管理那么这个只是一个线性地址,还需要通过页表来寻址到真实的物理内存。

32位CPU专门新赠了一个CR3寄存器用来完成分页式管理,通过CR3寄存器可以寻址到页目录表,然后再将32位线性地址的高10位作为页目录表的索引,通过这个索引可以找到相应的页表,再将中间10为作为页表的索引,通过这个索引可以寻址到对应物理内存的起始地址,最后通过这个其实地址和最后低12位的偏移地址找到对应真实内存。下面是这个过程的一个示例图:

为什么要使用分页式管理,直接让那个32位线性地址对应真实的内存不可以吗。当然可以,但是分页式管理也有它自身的优点:

1. 可以实现页面的保护:系统通过设置相关属性信息来指定特权级别和其他状态

2. 可以实现物理内存的共享:从上面的图中可以看出,不同的线性地址是可以映射到相同的物理内存上的,只需要更改页表中对应的物理地址就可以实现不同的线性地址对应相同的物理内存实现内存共享。

3. 可以方便的实现虚拟内存的支持:在系统中有一个pagefile.sys的交互页面文件,这个是系统用来进行内存页面与磁盘进行交互,以应对内存不够的情况。系统为每个内存页维护了一个值,这个值表示该页面多久未被访问,当页面被访问这个值被清零,否则每过一段时间会累加一次。当这个值到达某个阈值时,系统将页面中的内容放入磁盘中,将这块内存空余出来以便保存其他数据,同时将之前的线性地址做一个标记,表名这个线性地址没有对应到具体的内存中,当程序需要再次访问这个线性地址所对应的内存时系统会再次将磁盘中的数据写入到内存中。虽说这样做相当于扩大了物理内存,但是磁盘相对于内存来说是一个慢速设备,在内存和磁盘间进行数据交换总是会耗费大量的时间,这样会拖慢程序运行,而采用SSD硬盘会显著提高系统运行效率,就在于SSD提高了与内存进行数据交换的效率。如果想显著提高效率,最好的办法是加内存毕竟在内存和硬盘间倒换数据是要话费时间的。

保护模式

在以前的16位CPU中采用的多是实模式,程序中使用的地址都是真实的物理地址,这样如果内存分配不合理,会造成一个程序将另外一个程序所在的内存覆盖这样对另外一个程序将造成严重影响,但是在32位保护模式下,不再会产生这种问题,保护模式将每个进程的地址空间隔离开来,还记得上面的LDT吗,在不同的程序中即使采用的是相同的地址,也会被LDT映射到不同的线性地址上。

保护模式主要体现在这样几个方面:

1.同一进程中,使用4个不同访问级别的内存段,对每个页面的访问属性做了相应的规定,防止错误访问的情况,同时为提供了4中不同代码特权,0特权的代码可以访问任意级别的内存,1特权能任意访问1…3级内存,但不能访问0级内存,依次类推。通常这些特权级别叫做ring0-ring3。

2. 对于不同的进程,将他们所用到的内存等资源隔离开来,一个进程的执行不会影响到另一个进程。

windows系统的内存管理

windows内存管理器

我们将系统中实际映射到具体的实际内存上的页面称为工作集。当进程想访问多余实际物理内存的内存时,系统会启用虚拟内存管理机制(工作集管理),将那些长时间未访问的物理页面复制到硬盘缓冲文件上,并释放这些物理页面,映射到虚拟空间的其它页面上;系统的内存管理器主要由下面的几个部分组成:

1. 工作集管理器(优先级16):这个主要负责记录每个页面的年龄,也就有多久未被访问,当页面被访问这个年龄被清零,否则每过一段时间就进行累加1的操作。

2. 进程/栈交换器(优先级23):主要用于在进行进程或者线程切换时保存寄存器中的相关数据用以保存相关环境。

3. 已修改页面写出器(优先级17):当内存映射的内容发生改变时将这个改变及时的写入到硬盘中,防止由于程序意外终止而造成数据丢失

4. 映射页面写出器(优先级17):当页面的年龄达到一定的阈值时,将页面内容写入到硬盘中

5. 解引用段线程(优先级18):释放以写入到硬盘中的空闲页面

6. 零页面线程(优先级0):将空闲页面清零,以便程序下次使用,这个线程保证了新提交的页面都是干净的零页面

进程虚拟地址空间的布局

windows为每个进程提供了平坦的4GB的线性地址空间,这个地址空间被分为用户分区和内核分区,他们各占2GB大小,其中内核分区在高地址位,用户分区在低地址位,下面是内存分布的一个表格:

分区 地址范围
NULL指针区 0x00000000-0x0000FFFF
用户分区 0x00010000-0x7FFEFFFF
64K禁入区 0x7FFF0000-0x7FFFFFFF
内核分区 0x80000000-0xFFFFFFFF

从上面的图中可以看出,系统的内核分区是2GB而用户可用的分区并没有2GB,在用户分区的头64K和尾部的64K不允许用户使用。

另外我们可以压缩内核分区的大小,以便使用户分区占更多的内存,这就是/3GB方式,下面是这种方式的具体内存分布:

分区 地址范围
NULL指针区 0x00000000-0x0000FFFF
用户分区 0x00010000-0xBFFEFFFF
64K禁入区 0xBFFF0000-0xBFFFFFFF
内核分区 0xC0000000-0xFFFFFFFF

windows虚拟内存管理函数

VirtualAlloc

VirtualAlloc函数主要用于提交或者保留一段虚拟地址空间,通过该函数提交的页面是经过0页面线程清理的干净的页面。

LPVOID VirtualAlloc(
  LPVOID lpAddress, //虚拟内存的地址
  DWORD dwSize, //虚拟内存大小
  DWORD flAllocationType,//要对这块的虚拟内存做何种操作
  DWORD flProtect //虚拟内存的保护属性
); 

我们可以指定第一个参数来告知系统,我们希望操作哪块内存,如果这个地址对应的内存已经被保留了那么将向下偏移至64K的整数倍,如果这块内存已经被提交,那么地址将向下偏移至4K的整数倍,也就是说保留页面的最小粒度是64K,而提交的最小粒度是一页4K。

第三个参数是指定分配的类型,主要有以下几个值

含义
MEM_COMMIT 提交,也就是说将虚拟地址映射到对应的真实物理内存中,这样这块内存就可以正常使用
MEM_RESERVE 保留,告知系统以这个地址开始到后面的dwSize大小的连续的虚拟内存程序要使用,进程其他分配内存的操作不得使用这段内存。
MEM_TOP_DOWN 从高端地址保留空间(默认是从低端向高端搜索)
MEM_LARGE_PAGES 开启大页面的支持,默认一个页面是4K而大页面是2M(这个视具体系统而定)
MEM_WRITE_WATCH 开启页面写入监视,利用GetWriteWatch可以得到写入页面的统计情况,利用ResetWriteWatch可以重置起始计数
MEM_PHYSICAL 用于开启PAE

第四个参数主要是页面的保护属性,参数可取值如下:

含义
PAGE_READONLY 只读
PAGE_READWRITE 可读写
PAGE_EXECUTE 可执行
PAGE_EXECUTE_READ 可读可执行
PAGE_EXECUTE_READWRITE 可读可写可执行
PAGE_NOACCESS 不可访问
PAGE_GUARD 将该页设置为保护页,如果试图对该页面进行读写操作,会产生一个STATUS_GUARD_PAGE 异常

下面是该函数使用的几个例子:

1. 页面的提交/保留与释放

//保留并提交
    LPVOID pMem = VirtualAlloc(NULL, 4 * 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    srand((unsigned int)time(NULL));

    float* pfMem = (float*)pMem;
    for (int i = 0; i < 4 * 4096 / sizeof(float); i++)
    {
        pfMem[i] = rand();
    }

    //释放
    VirtualFree(pMem, 4 * 4096, MEM_RELEASE);

    //先保留再提交
    LPBYTE pByte = (LPBYTE)VirtualAlloc(NULL, 1024 * 1024, MEM_RESERVE, PAGE_READWRITE);
    VirtualAlloc(pByte + 4 * 4096, 4096, MEM_COMMIT, PAGE_READWRITE);
    pfMem = (float*)(pByte + 4 * 4096);
    for (int i = 0; i < 4096/sizeof(float); i++)
    {
        pfMem[i] = rand();
    }

    //释放
    VirtualFree(pByte + 4 * 4096, 4096, MEM_DECOMMIT);
    VirtualFree(pByte, 1024 * 1024, MEM_RELEASE);
  1. 大页面支持
//获得大页面的尺寸
DWORD dwLargePageSize = GetLargePageMinimum();
LPVOID pBuffer = VirtualAlloc(NULL, 64 * dwLargePageSize, MEM_RESERVE, PAGE_READWRITE);
//提交大页面
VirtualAlloc(pBuffer, 4 * dwLargePageSize, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
VirtualFree(pBuffer, 4 * dwLargePageSize, MEM_DECOMMIT);
VirtualFree(pBuffer, 64 * dwLargePageSize, MEM_RELEASE);

VirtualProtect

VirtualProtect用来设置页面的保护属性,函数原型如下:

BOOL VirtualProtect(
  LPVOID lpAddress, //虚拟内存地址
  DWORD dwSize, //大小
  DWORD flNewProtect, //保护属性
  PDWORD lpflOldProtect //返回原来的保护属性
); 

这个保护属性与之前介绍的VirtualAlloc中的保护属性相同,另外需要注意的一点是一般返回原来的属性的话,这个指针可以为NULL,但是这个函数不同,如果第四个参数为NULL,那么函数调用将会失败

LPVOID pBuffer = VirtualAlloc(NULL, 4 * 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
float *pfArray = (float*)pBuffer;
for (int i = 0; i < 4 * 4096 / sizeof(float); i++)
{
    pfArray[i] = 1.0f * rand();
}

//将页面改为只读属性
DWORD dwOldProtect = 0;
VirtualProtect(pBuffer, 4 * 4096, PAGE_READONLY, &dwOldProtect);
//写入数据将发生异常
pfArray[9] = 0.1f;
VirtualFree(pBuffer, 4 * 4096, MEM_RELEASE);

VirtualQuery

这个函数用来查询某段虚拟内存的属性信息,这个函数原型如下:

DWORD VirtualQuery(
  LPCVOID lpAddress,//地址
  PMEMORY_BASIC_INFORMATION lpBuffer, //用于接收返回信息的指针
  DWORD dwLength //缓冲区大小,上述结构的大小
); 

结构MEMORY_BASIC_INFORMATION的定义如下:

typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID BaseAddress; //该页面的起始地址
    PVOID AllocationBase;//分配给该页面的首地址
    DWORD AllocationProtect;//页面的保护属性
    DWORD RegionSize; //页面大小
    DWORD State;//页面状态
    DWORD Protect;//页面的保护类型
    DWORD Type;//页面类型
} MEMORY_BASIC_INFORMATION;
typedef MEMORY_BASIC_INFORMATION *PMEMORY_BASIC_INFORMATION; 

AllocationProtect与Protect所能取的值与之前的保护属性的值相同。

State的取值如下:

MEM_FREE:空闲

MEM_RESERVE:保留

MEM_COMMIT:已提交

Type的取值如下:

MEM_IMAGE:映射类型,一般是映射到地址控件的可执行模块如DLL,EXE等

MEM_MAPPED:文件映射类型

MEM_PRIVATE:私有类型,这个页面的数据为本进程私有数据,不能与其他进程共享

下面是这个的使用例子:

#include<windows.h>
#include <stdio.h>
#include <tchar.h>
#include <atlstr.h>

CString GetMemoryInfo(MEMORY_BASIC_INFORMATION *pmi);
int _tmain(int argc, TCHAR *argv[])
{
    SYSTEM_INFO sm = {0};
    GetSystemInfo(&sm);
    LPVOID dwMinAddress = sm.lpMinimumApplicationAddress;
    LPVOID dwMaxAddress = sm.lpMaximumApplicationAddress;

    MEMORY_BASIC_INFORMATION mbi = {0};
    _putts(_T("BaseAddress\tAllocationBase\tAllocationProtect\tRegionSize\tState\tProtect\tType\n"));

    for (LPVOID pAddress = dwMinAddress; pAddress <= dwMaxAddress;)
    {
        if (VirtualQuery(pAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
        {
            break;
        }

        _putts(GetMemoryInfo(&mbi));
        //一般通过BaseAddress(页面基地址) + RegionSize(页面长度)来寻址到下一个页面的的位置
        pAddress = (BYTE*)mbi.BaseAddress + mbi.RegionSize;
    }

}

CString GetMemoryInfo(MEMORY_BASIC_INFORMATION *pmi)
{
    CString lpMemoryInfo = _T("");

    int iBaseAddress = (int)(pmi->BaseAddress);
    int iAllocationBase = (int)(pmi->AllocationBase);

    CString szProtected = _T("\0");
    if (pmi->Protect & PAGE_READONLY)
    {
        szProtected = _T("R");
    }else if (pmi->Protect & PAGE_READWRITE)
    {
        szProtected = _T("RW");
    }else if (pmi->Protect & PAGE_WRITECOPY)
    {
        szProtected = _T("WC");
    }else if (pmi->Protect & PAGE_EXECUTE)
    {
        szProtected = _T("X");
    }else if (pmi->Protect & PAGE_EXECUTE_READ)
    {
        szProtected = _T("RX");
    }else if (pmi->Protect & PAGE_EXECUTE_READWRITE)
    {
        szProtected = _T("RWX");
    }else if (pmi->Protect & PAGE_EXECUTE_WRITECOPY)
    {
        szProtected = _T("WCX");
    }else if (pmi->Protect & PAGE_GUARD)
    {
        szProtected = _T("GUARD");
    }else if (pmi->Protect & PAGE_NOACCESS)
    {
        szProtected = _T("NOACCESS");
    }else if (pmi->Protect & PAGE_NOCACHE)
    {
        szProtected = _T("NOCACHE");
    }else
    {
        szProtected = _T(" ");
    }

    CString szAllocationProtect = _T("\0");
    if (pmi->AllocationProtect & PAGE_READONLY)
    {
        szProtected = _T("R");
    }else if (pmi->AllocationProtect & PAGE_READWRITE)
    {
        szProtected = _T("RW");
    }else if (pmi->AllocationProtect & PAGE_WRITECOPY)
    {
        szProtected = _T("WC");
    }else if (pmi->AllocationProtect & PAGE_EXECUTE)
    {
        szProtected = _T("X");
    }else if (pmi->AllocationProtect & PAGE_EXECUTE_READ)
    {
        szProtected = _T("RX");
    }else if (pmi->AllocationProtect & PAGE_EXECUTE_READWRITE)
    {
        szProtected = _T("RWX");
    }else if (pmi->AllocationProtect & PAGE_EXECUTE_WRITECOPY)
    {
        szProtected = _T("WCX");
    }else if (pmi->AllocationProtect & PAGE_GUARD)
    {
        szProtected = _T("GUARD");
    }else if (pmi->AllocationProtect & PAGE_NOACCESS)
    {
        szProtected = _T("NOACCESS");
    }else if (pmi->AllocationProtect & PAGE_NOCACHE)
    {
        szProtected = _T("NOCACHE");
    }else
    {
        szProtected = _T(" ");
    }

    DWORD dwRegionSize = pmi->RegionSize;
    CString strState = _T("");
    if (pmi->State & MEM_FREE)
    {
        strState = _T("Free");
    }else if (pmi->State & MEM_RESERVE)
    {
        strState = _T("Reserve");
    }else if (pmi->State & MEM_COMMIT)
    {
        strState = _T("Commit");
    }else
    {
        strState = _T(" ");
    }

    CString strType = _T("");
    if (pmi->Type & MEM_IMAGE)
    {
        strType = _T("Image");
    }else if (pmi->Type & MEM_MAPPED)
    {
        strType = _T("Mapped");
    }else if (pmi->Type & MEM_PRIVATE)
    {
        strType = _T("Private");
    }

    lpMemoryInfo.Format(_T("%08X %08X %s %d %s %s %s\n"), iBaseAddress, iAllocationBase, szAllocationProtect, dwRegionSize, strState, szProtected, strType);
    return lpMemoryInfo;
}

VirtualLock和VirtualUnlock

这两个函数用于锁定和解锁页面,前面说过操作系统会将长时间不用的内存中的数据放入到系统的磁盘文件中,需要的时候再放回到内存中,这样来回倒腾,必定会造成程序效率的底下,为了避免这中效率底下的操作,可以使用VirtualLock将页面锁定在内存中,防止页面交换,但是不用了的时候需要使用VirtualUnlock来解锁,不然一直锁定而不解锁会造成真实内存的不足。

另外需要注意的是,不能一次操作超过工作集规定的最大虚拟内存,这样会造成程序崩溃,我们可以通过函数SetProcessWorkingSetSize来设置工作集规定的最大虚拟内存的大小。下面是一个使用例子:

SetProcessWorkingSetSize(GetCurrentProcess(), 1024 * 1024, 2 * 1024 * 1024);
LPVOID pBuffer = VirtualAlloc(NULL, 4 * 4096, MEM_RESERVE, PAGE_READWRITE);
//不能锁定超过进程工作集大小的虚拟内存
VirtualLock(pBuffer, 3 * 1024 * 1024);
//不能一次提交超过进程工作集大小的虚拟内存
VirtualAlloc(pBuffer, 3 * 1024 * 1024, MEM_COMMIT, PAGE_READWRITE);
float *pfArray = (float*)pBuffer;
for (int i = 0; i < 4096 / sizeof(float); i++)
{
    pfArray[i] = 1.0f * rand();
}

VirtualUnlock(pBuffer, 4096);
VirtualFree(pBuffer, 4096, MEM_DECOMMIT);
VirtualFree(pBuffer, 4 * 4096, MEM_RELEASE);

VirtualFree

VirtualFree用于释放申请的虚拟内存。这个函数支持反提交和释放,这两个操作由第三个参数指定:

MEM_DECOMMIT:反提交,这样这个线性地址就不再映射到具体的物理内存,但是这个地址仍然是保留地址。

MEM_RELEASE:释放,这个范围的地址不再作为保留地址

时间: 2024-11-05 13:32:59

windows虚拟内存管理的相关文章

Windows内存管理的方式

一.内存的概念 1. 物理内存:即插在主板上的内存条.他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外). 但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存占用,甚至导致物理内存消耗殆尽. 2. 虚拟内存:虚拟内存就是在硬盘上划分一块页面文件,充当内存. 当程序在运行时,有一部分资源还没有用上或者同时打开几个程序却只操作其中一个程序时,系统没必要将程序所有的资源都塞在物理内存中,于是,系统将这些暂时不用的资源放在虚拟内存上,等到需要时在调出来用. 当程序运行时需要

windows 进程管理器中的内存是什么意思?

*内存 - 工作集:私人工作集中的内存数量与进程正在使用且可以由其他进程共享的内存数量的总和. *内存 - 峰值工作集:进程所使用的工作集内存的最大数量. *内存 - 工作集增量:进程所使用的工作集内存中的更改量. *内存 - 私人工作集:工作集的子集,它专门描述了某个进程正在使用且无法与其他进程共享的内存数量. *内存 - 提交大小:为某进程使用而保留的虚拟内存的数量. *内存 - 页面缓冲池:由内核或驱动程序代表进程分配的可分页内核内存的数量.可分页内存是可写入其他存储媒体(例如硬盘)的内存

windows虚拟内存(win32)

windows是一个以虚拟内存为基础的操作系统.在这种系统环境下,windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要.对象被移动意味着它的地址变化了.如果地址总是如此变化,我们该到哪里去找对象呢? 为了解决这个问题,Windows操作系统为各应用程序腾出一些内存存储地址,用来专门登记个应用在内存中的地址变化,(所以句柄是指向地址的地址.)句柄所记录的就是内存管理器中对象不断改编后的地址. 这样我们只需记住这个句柄地址就可以简介地知道对象具体在内存中的哪个位置.这个

windows内存详解(一) 全面介绍Windows内存管理机制及C++内存分配实例

十分感谢MS社区的帖子,讲得很好~ http://social.technet.microsoft.com/Forums/zh-CN/2219/thread/afc1269f-fe08-4dc7-bb94-c395d607e536 (一):进程空间 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本文目的: 对Windows内存管理机制了解清楚,有效的利用C+

cmd命令行修改windows虚拟内存pagefile.sys

cmd命令行修改windows虚拟内存 #查看当前设置wmic pagefile list /format:list #取消自动管理分页文件大小wmic computersystem where name="%computername%" set AutomaticManagedPagefile=False #修改页面文件大小 最小1024MB,最大4096MBwmic pagefileset where name="C:\pagefile.sys" set Ini

终于懂了:FWinControls子控件的显示是由Windows来管理,而不是由Delphi来管理

在研究TCustomControl的显示过程中,怎么样都找不到刷新FWinControls并重新显示的代码: procedure TWinControl.PaintHandler(var Message: TWMPaint); var I, Clip, SaveIndex: Integer; DC: HDC; PS: TPaintStruct; begin DC := Message.DC; if DC = 0 then DC := BeginPaint(Handle, PS); try if

计算机操作系统学习笔记_8_内存管理 --虚拟内存管理

td p { margin-bottom: 0cm; }h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { font-family: "微软雅黑"; font-size: 16pt; }h2.ctl { font-family: "AR PL UMing CN"; font-size: 16pt; }h1 { margin-botto

虚拟内存管理

操作系统利用体系结构提供的VA到PA的转换机制实现虚拟内存管理.有了共享库的基础之后我们可以进一步理解虚拟内存管理了.首先分析例子: [实际与上图存在出入,为方便下面的描述采用原书截图] 用ps命令查看当前终端下的进程,得知bash的进程id是29977,然后用cat /proc/29977/maps命令查看他的虚拟地址空间./proc目录中的文件并不是真正的磁盘文件,而是由内核虚拟出来的文件系统,当前系统中运行的每一个进程在/proc下都有一个子目录,目录名就是该进程的id,查看目录下的文件可

linux内核内存分配(三、虚拟内存管理)

在分析虚拟内存管理前要先看下linux内核内存的详细分配我开始就是困在这个地方,对内核内存的分类不是很清晰.我摘录其中的一段: 内核内存地址 =========================================================================================================== 在linux的内存管理中,用户使用0-3GB的地址空间,而内核只是用了3GB-4GB区间的地址空间,共1GB:非连 续空间的物理映射就位于3G