【面经笔记】内存映射、共享内存

来源:互联网 发布:cad专业绘图软件 编辑:程序博客网 时间:2024/06/16 17:32

知道共享内存吗?

额,是把同一块内存映射到两个进程的地址空间?。。。。。。

(还不如说不知道)


内存映射

内存映射主要应用于三种情况:

  • 系统使用内存映射文件载入EXE,DLL文件。这节省了大量页交换文件的空间以及程序启动时间。
  • 开发人员使用内存映射文件来访问磁盘上的数据文件,这使得我们避免直接对文件I/O操作和文件内容的缓存
  • 通过使用内存映射文件,可以实现不同进程间的共享数据(进程间共享内存通信)

创建一个内存映射文件相当于先预订一块地址空间区域,然后再给区域调拨物理存储器。唯一的不同在于内存映射文件的物理存储器来自磁盘上的文件,而不是系统的页交换文件中分配(内存)。


转载可执行文件:

CreateProcess时,系统执行以下步骤:

1、找到exe文件位置
2、系统创建新的内核对象
3、系统为新进程创建私有地址空间
4、系统在地址空间预定一块足够大区域容纳exe文件,默认基地址0x400000。
5、系统会标注exe所在的地址空间区域。表面该区域的后备物理存储器来自磁盘的文件而不是来自系统的页交换文件。(即在磁盘,不在内存

6、当系统把exe文件映射到进程地址空间后,会访问exe的一个段,获取DLL加载信息,系统调用LoadLibrary()加载每个DLL。每次加载DLL步骤都类似上述4、5。

区别是exe是第一个加载的模块,基地址不会被占用,而DLL的基地址可能已经被占用,需要对DLL进行重定位,修改部分指令。

系统同样会标注DLL的地址空间区域,表明该区域的后备物理存储器来自磁盘的DLL文件,而不是系统的页交换文件。但是如果执行了重定位,则会标注被修改的部分物理存储器被映射到了页交换文件。


同一可执行文件/dll 的多个实例不会共享全局/静态数据

通过内存映射文件,同一应用程序的多个实例可以共享内存中的代码和数据。

系统通过写时复制特性防止应用程序的一个实例修改了数据页面的全部变量,影响其他实例。

写时复制:任何程序试图写入内存映射文件时,系统首先截获,然后为应用程序分配一块新内存页,赋值页面内容,最后让应用程序写入刚分配的新内存页。


使用内存映射文件

三个步骤:
1、创建或者打开一个文件的内核对象,该对象标识了我们想要用作为内存映射文件的那个磁盘文件
2、创建一个文件映射内核对象,来告诉系统文件的大小以及我们打算如何访问文件
3、告诉系统把文件映射对象的部分或全部映射到进程的地址空间中。


创建或者打开文件内核对象

CreateFile()函数创建或者打开一个文件内核对象。返回文件内核对象的句柄。

调用CreateFile()是为了告诉操作系统文件映射的物理存储器所在位置。传入的路径是文件在磁盘上所在的位置,文件映射对象的物理存储器来自该文件。

创建文件映射内核对象

调用CreateFileMapping()告诉系统文件映射对象需要多大的物理存储器。指定访问属性。

int main(){    HANDLE hFile = CreateFile(TEXT("MMFTest.Dat"), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);    //文件大小为0byte    HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 100, NULL);    //文件大小变为100byte    CloseHandle(hFile);    CloseHandle(hFileMap);    return 0;}

将文件的数据映射到进程的地址空间

创建了文件映射对象后,需要为文件的数据预订一块地址空间并将磁盘文件数据作为物理存储器拨给地址空间区域。完成映射。

MapViewOfFile()函数。
MapViewOfFileEx()函数可以把文件映射到指定的基地址。

当把一个文件映射到进程的地址空间中时,不必一下子映射整个文件,可以每次只把文件的一小部分映射到地址空间中,文件中被映射到进程地址空间的部分被称为视图,其名称MapViewOfFile()来源于此。

把一个文件的一个视图映射到进程的地址空间中,需要告诉操作系统两件事情:

1、把数据文件的哪个字节映射到视图的第一个字节(起点)
2、把数据文件的多少映射到地址空间中去(大小)

若指定FILE_MAP_COPY,则类似写时复制机制,不会修改原始数据文件。


从进程地址空间撤销对文件数据的映射

UnmapViewOfFile(),撤销映射,释放预订的地址空间区域。

如果需要将缓存中的修改写入磁盘,需要调用FlushViewOfFile()。


由于可以只映射文件的一个视图,而这个视图只是文件的一个小部分数据,故完成对文件第一个视图访问后,撤销对文件这一部分的映射,然后把另一部分映射到视图中。可以处理超大文件。

系统允许把同一个文件中的数据映射到多个视图中,如果把多个进程把同一数据文件映射到多个视图中,数据会保持一致,因为数据文件中的每个页面在内存中只有一份,但被映射到了多个进程的地址空间中。

第一个进程调用MapViewOfFile()返回的内存地址,与第二个进程调用MapViewOfFile()返回的内存地址很可能不相同。即使两个进程都映射同一个文件映射对象的视图。


用内存映射文件在进程间共享数据

共享机制:多个进程映射同一个文件映射对象的视图。注意,对多个进程共享同一个文件映射对象来说,所有进程使用的文件映射对象的名称必须完全相同。

同所有内核对象一样,可以通过:继承,命名,复制三种方式跨进程共享对象。


共享内存

以页交换文件为后备存储器的内存映射文件

创建一个内存映射文件相当于先预订一块地址空间区域,然后再给区域调拨物理存储器。唯一的不同在于内存映射文件的物理存储器是系统的页交换文件中分配(内存)。

不需要调用CreateFile,只需要调用CreateFileMapping()函数,将INVALID_HANDLE_VALUE作为hFile参数传入。告诉操作系统我们创建的是内存映射,不是磁盘文件映射。

  • 例:

写一个创建共享内存,并写入数据

    char* pData = NULL;    HANDLE  hFileMap = NULL;    hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, _T("WndData"));    if (!hFileMap)  // 不存在则创建      {        hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, _T("WndData"));    }    if (hFileMap != NULL)    {        pData = (char*)MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);          if (pData == NULL)        {            CloseHandle(hFileMap);            hFileMap = NULL;        }    }    HANDLE hMutex = CreateMutex(NULL, TRUE, _T("WndMutex"));    char* strValue = "123abcpStr";    //pData = strValue;      memcpy(pData, strValue, strlen(strValue));    FlushViewOfFile(pData, sizeof(HWND*));      ReleaseMutex(hMutex);

读取共享数据:

 HANDLE hMutex = NULL;    while (true)    {        hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, _T("WndMutex"));        if (NULL != hMutex)        {            break;        }    }    WaitForSingleObject(hMutex, INFINITE);    HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, _T("WndData"));    char* pData = (char*)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024);    char* strTemp = pData;    cout<<strTemp;    UnmapViewOfFile(pData);    ReleaseMutex(hMutex);

虚拟地址如何转化为物理地址

操作系统为每个进程维持了一个页表,即独立的虚拟地址空间。

页表会将虚拟地址空间页映射到物理页。每次地址翻译硬件硬件一个地址转化为物理地址时都会读取页表。

页表中每个条目由一个有效位和地址字段组成。有效位表明了该物理页是否被缓存在内存中。

如果设置了有效位,那么地址字段就指向内存中相应的物理页起始位置。这个内存物理页中缓存了该虚拟页。

如果没有设置有效位,若是一个空地址则表明这个虚拟页还未被分配,否则,这个地址就指向虚拟页在磁盘的起始位置。

原创粉丝点击