???????虚拟内存抽象使应用程序能够拥有一个独立而连续的虚拟地址空间,其通过页表与硬件的配合能够在对应用程序透明的前提下自动地进行虚拟地址到物理地址的翻译。除此之外,虚拟内存抽象还带来了其它的功能。
一、共享内存
共享内存允许通一个物理页在不同的应用程序中共享,如下图 ???????应用程序A的虚拟页V1和应用程序B的虚拟页V2都被映射到物理页P,则物理页P是应用程序A和B的共享内存。此时程序A读取虚拟页V1和程序B读取虚拟页V2将得到相同的内容。互相也能看到对方所修改的内容。其中最重要的一个用途就是可以让不同的应用程序之间传递数据。基于共享内存的思想,操作系统又从中衍生出,写时拷贝、内存去重等功能。 下面是一个c++的例子 服务端:
#include <windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
int main()
{
char szBuffer[] = "锄禾日当午!";
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
BUF_SIZE,
L"ShareMemoryTy"
);
LPVOID lpBase = MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
BUF_SIZE
);
strcpy((char*)lpBase, szBuffer);
cout << "服务端:" << (char*)lpBase << endl;
Sleep(20000);
UnmapViewOfFile(lpBase);
CloseHandle(hMapFile);
return 0;
}
客户端:
#include <iostream>
#include <windows.h>
using namespace std;
#define BUF_SIZE 4096
int main()
{
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, L"ShareMemoryTy");
if (hMapFile) {
LPVOID lpBase = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
char szBuffer[BUF_SIZE] = { 0 };
strcpy_s(szBuffer, (char*)lpBase);
cout << "客户:" << szBuffer << endl;
UnmapViewOfFile(lpBase);
CloseHandle(hMapFile);
}
else {
cout << "打开共享失败!" << endl;
}
system("pause");
return 0;
}
结果:
二、写时拷贝
???????写时拷贝技术允许应用程序A和应用程序B以只读的方式共享一段物理内存,一旦某个应用程序对该内存区域进行修改,就会触发缺页异常。注意,这里的缺页异常是否与违反权限导致的,不是之前所说的换页机制下的缺页异常是由于未映射导致的。在触发了缺页异常后,CPU同样会将控制流传递给操作系统预先设置的缺页异常处理函数,在该函数中操作系统会发现是缺页异常由于写了只读内存造成的,而且相应的内存区域又被系统标记为写时拷贝。于是操作系统会在物理内存中将缺页异常对应的物理页重新拷贝一份,并且将新拷贝的物理页以可读可写的方式重新映射给触发异常的应用程序,此后再恢复应用程序的执行。比如两个应用程序拥有很多相同的内存数据(比如加载了相同的动态链接库),如果把这些数据相同的内存页在物理内存中仅存一份,然后以只读的方式映射给两个应用程序,那么就能显著地节约物理内存。 接下来看例子:
#include <iostream>
#include <Windows.h>
#pragma warning (disable:4996)
using namespace std;
class String
{
public:
String(const char* pStr = "")
:_pStr(new char[strlen(pStr) + 4 + 1])
{
if (NULL == pStr)
{
*((int*)_pStr) = 1;
_pStr += 4;
*_pStr = '\0';
}
else
{
*((int *)_pStr) = 1;
_pStr += 4;
strcpy(_pStr, pStr);
}
}
String(const String& s)
:_pStr(s._pStr)
{
++(*(int*)(_pStr - 4));
}
~String()
{
if (NULL == _pStr)
{
return;
}
else
{
if (--(*(int*)(_pStr - 4)) == 0)
{
delete[](_pStr - 4);
_pStr = NULL;
}
}
}
String& operator=(const String& s)
{
if (_pStr != s._pStr)
{
if (--(*(int*)(_pStr - 4)) == 0)
{
delete[](_pStr - 4);
_pStr = NULL;
}
_pStr = s._pStr;
++(*(int*)(_pStr - 4));
}
return *this;
}
char& operator[](size_t t)
{
if (t >= 0 && t < strlen(_pStr))
{
if ((*(int*)(_pStr - 4)) > 1)
{
char *pTemp = new char[strlen(_pStr) + 4 + 1];
pTemp += 4;
strcpy(pTemp, _pStr);
--(*(int*)(_pStr - 4));
_pStr = pTemp;
*((int*)(_pStr - 4)) = 1;
}
return _pStr[t];
}
}
friend std::ostream & operator<<(std::ostream & out, String & A)
{
printf("%s %p\n", A._pStr, A._pStr);
return out;
}
private:
char *_pStr;
};
void FunTest()
{
String s1("Hello world");
std::cout << s1;
String s2(s1);
std::cout << s2;
String s3 = s2;
s3[3] = 'm';
std::cout << s3;
}
int main()
{
FunTest();
system("pause");
return 0;
}
结果:
三、内存去重
???????基于写时拷贝机制,操作系统进一步设计了内存去重的功能。操作系统可以定期地在内存中扫描具有相同内容的物理页,并且找到映射到这些物理页的虚拟页,然后只保留其中一个物理页,并将具有相同的内容的其它虚拟页都用写时拷贝的方式映射到这个物理页,然后释放其它物理页以供将来使用。该功能通常由操作系统主动发起。
|