Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Рихтер Дж., Назар К. - Windows via C C++. Программирование на языке Visual C++ - 2009

.pdf
Скачиваний:
6266
Добавлен:
13.08.2013
Размер:
31.38 Mб
Скачать

Глава 15. Использование виртуальной памяти в приложениях.docx 517

//Put some default text in the 2nd storage block g_aws[1].MapStorage(g_aw[0]);

_tcscpy_s((PTSTR) (PVOID) g_aw[0], g_cbBufferSize, TEXT("Text in Storage 1"));

//Populate the dialog box controls

for (int n = 0; n <= 1; n++) {

// Set the combo box for each address window

int id = ((n == 0) ? IDC_WINDOW0STORAGE : IDC_WINDOW1STORAGE); HWND hWndCB = GetDlgItem(hWnd, id); ComboBox_AddString(hWndCB, TEXT("No storage")); ComboBox_AddString(hWndCB, TEXT("Storage 0")); ComboBox_AddString(hWndCB, TEXT("Storage 1"));

// Window 0 shows Storage 0, Window 1 shows Storage 1 ComboBox_SetCurSel(hWndCB, n + 1);

FORWARD_WM_COMMAND(hWnd, id, hWndCB, CBN_SELCHANGE, SendMessage); Edit_LimitText(GetDlgItem(hWnd,

(n == 0) ? IDC_WINDOW0TEXT : IDC_WINDOW1TEXT), g_nChars);

}

return(TRUE);

}

///////////////////////////////////////////////////////////////////////////////

void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {

switch (id) {

case IDCANCEL: EndDialog(hWnd, id); break;

case IDC_WINDOW0STORAGE: case IDC_WINDOW1STORAGE:

if (codeNotify == CBN_SELCHANGE) {

// Show different storage in address window

int nWindow = ((id == IDC_WINDOW0STORAGE) ? 0 : 1); int nStorage = ComboBox_GetCurSel(hWndCtl) - 1;

if (nStorage == -1) { // Show no storage in this window chVERIFY(g_aw[nWindow].UnmapStorage());

} else {

518 Часть III. Управление памятью

if (!g_aws[nStorage].MapStorage(g_aw[nWindow])) {

// Couldn't map storage in window chVERIFY(g_aw[nWindow].UnmapStorage()); ComboBox_SetCurSel(hWndCtl, 0); // Force "No storage" chMB("This storage can be mapped only once.");

}

}

//Update the address window's text display HWND hWndText = GetDlgItem(hWnd,

((nWindow == 0) ? IDC_WINDOW0TEXT : IDC_WINDOW1TEXT)); MEMORY_BASIC_INFORMATION mbi;

VirtualQuery(g_aw[nWindow], &mbi, sizeof(mbi));

//Note: mbi.State == MEM_RESERVE if no storage is in address window EnableWindow(hWndText, (mbi.State == MEM_COMMIT)); Edit_SetText(hWndText, IsWindowEnabled(hWndText)

?(PCTSTR) (PVOID) g_aw[nWindow] : TEXT("(No storage)"));

}

break;

case IDC_WINDOW0TEXT: case IDC_WINDOW1TEXT:

if (codeNotify == EN_CHANGE) {

// Update the storage in the address window

int nWindow = ((id == IDC_WINDOW0TEXT) ? 0 : 1); Edit_GetText(hWndCtl, (PTSTR) (PVOID) g_aw[nWindow], g_nChars);

}

break;

}

}

///////////////////////////////////////////////////////////////////////////////

INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

switch (uMsg) {

chHANDLE_DLGMSG(hWnd, WM_INITDIALOG,

Dlg_OnInitDialog);

chHANDLE_DLGMSG(hWnd, WM_COMMAND,

Dlg_OnCommand);

}

 

 

 

return(FALSE);

 

}

///////////////////////////////////////////////////////////////////////////////

Глава 15. Использование виртуальной памяти в приложениях.docx 519

int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR, int) {

DialogBox(hInstExe, MAKEINTRESOURCE(IDD_AWE), NULL, Dlg_Proc); return(0);

}

//////////////////////////////// End of File //////////////////////////////////

AddrWindow.h

/****************************************************************************** Module: AddrWindow.h

Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre

******************************************************************************/

#pragma once

///////////////////////////////////////////////////////////////////////////////

#include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */ #include <tchar.h>

///////////////////////////////////////////////////////////////////////////////

class CSystemInfo : public SYSTEM_INFO { public:

CSystemInfo() { GetSystemInfo(this); }

};

///////////////////////////////////////////////////////////////////////////////

class CAddrWindow { public:

CAddrWindow() { m_pvWindow = NULL; } ~CAddrWindow() { Destroy(); }

BOOL Create(SIZE_T dwBytes, PVOID pvPreferredWindowBase = NULL) { // Reserve address window region to view physical storage m_pvWindow = VirtualAlloc(pvPreferredWindowBase, dwBytes,

MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE); return(m_pvWindow != NULL);

}

BOOL Destroy() { BOOL bOk = TRUE;

if (m_pvWindow != NULL) {

520 Часть III. Управление памятью

// Destroy address window region

bOk = VirtualFree(m_pvWindow, 0, MEM_RELEASE); m_pvWindow = NULL;

}

return(bOk);

}

BOOL UnmapStorage() {

// Unmap all storage from address window region MEMORY_BASIC_INFORMATION mbi; VirtualQuery(m_pvWindow, &mbi, sizeof(mbi)); return(MapUserPhysicalPages(m_pvWindow,

mbi.RegionSize / sm_sinf.dwPageSize, NULL));

}

// Returns virtual address of address window operator PVOID() { return(m_pvWindow); }

private:

PVOID m_pvWindow; // Virtual address of address window region static CSystemInfo sm_sinf;

};

///////////////////////////////////////////////////////////////////////////////

CSystemInfo CAddrWindow::sm_sinf;

///////////////////////////////////////////////////////////////////////////////

class CAddrWindowStorage { public:

CAddrWindowStorage() { m_ulPages = 0; m_pulUserPfnArray = NULL; } ~CAddrWindowStorage() { Free(); }

BOOL Allocate(ULONG_PTR ulBytes) {

//Allocate storage intended for an address window Free(); // Clean up this object's existing address window

//Calculate number of pages from number of bytes

m_ulPages = (ulBytes + sm_sinf.dwPageSize - 1) / sm_sinf.dwPageSize;

// Allocate array of page frame numbers m_pulUserPfnArray = (PULONG_PTR)

HeapAlloc(GetProcessHeap(), 0, m_ulPages * sizeof(ULONG_PTR));

Глава 15. Использование виртуальной памяти в приложениях.docx 521

BOOL bOk = (m_pulUserPfnArray != NULL); if (bOk) {

// The "Lock Pages in Memory" privilege must be enabled EnablePrivilege(SE_LOCK_MEMORY_NAME, TRUE);

bOk = AllocateUserPhysicalPages(GetCurrentProcess(), &m_ulPages, m_pulUserPfnArray);

EnablePrivilege(SE_LOCK_MEMORY_NAME, FALSE);

}

return(bOk);

}

BOOL Free() {

BOOL bOk = TRUE;

if (m_pulUserPfnArray != NULL) {

bOk = FreeUserPhysicalPages(GetCurrentProcess(), &m_ulPages, m_pulUserPfnArray);

if (bOk) {

// Free the array of page frame numbers HeapFree(GetProcessHeap(), 0, m_pulUserPfnArray); m_ulPages = 0;

m_pulUserPfnArray = NULL;

}

}

return(bOk);

}

ULONG_PTR HowManyPagesAllocated() { return(m_ulPages); }

BOOL MapStorage(CAddrWindow& aw) { return(MapUserPhysicalPages(aw,

HowManyPagesAllocated(), m_pulUserPfnArray));

}

BOOL UnmapStorage(CAddrWindow& aw) { return(MapUserPhysicalPages(aw,

HowManyPagesAllocated(), NULL));

}

private:

static BOOL EnablePrivilege(PCTSTR pszPrivName, BOOL bEnable = TRUE) {

BOOL bOk = FALSE;

// Assume function fails

HANDLE hToken;

 

 

 

// Try to open this process' access token if (OpenProcessToken(GetCurrentProcess(),

522 Часть III. Управление памятью

TOKEN_ADJUST_PRIVILEGES, &hToken)) {

// Attempt to modify the "Lock pages in Memory" privilege TOKEN_PRIVILEGES tp = { 1 };

LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid); tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); bOk = (GetLastError() == ERROR_SUCCESS);

CloseHandle(hToken);

}

return(bOk);

}

private:

ULONG_PTR m_ulPages; // Number of storage pages PULONG_PTR m_pulUserPfnArray; // Page frame number array

private:

static CSystemInfo sm_sinf;

};

///////////////////////////////////////////////////////////////////////////////

CSystemInfo CAddrWindowStorage::sm_sinf;

//////////////////////////////// End of File //////////////////////////////////

Оглавление

 

Г Л А В А 1 6 Стек потока.......................................................................................................

523

Функция из библиотеки С/С++ для контроля стека............................................................

528

Программа-пример Summation..............................................................................................

530

Г Л А В А 1 6

Стек потока

Иногда система сама резервирует какие-то регионы в адресном пространстве вашего процесса. Я уже упоминал в главе 13, что это делается для размещения блоков переменных окружения процесса и его потоков. Еще один случай резервирования региона самой системой — создание стека потока.

Всякий раз, когда в процессе создается поток, система резервирует регион адресного пространства для стека потока (у каждого потока свой стек) и передает этому региону какой-то объем физической памяти. По умолчанию система резервирует 1 Мб адресного пространства и передает ему всего две страницы памяти. Но стандартные значения можно изменить, указав при сборке программы параметр компоновщика /STACK либо параметр /F при компиляции:

/Freserve

/STACK:reserve[,commit]

При компоновке приложений размер стека, заданный параметром /STACK или /F, внедряется в РЕ-заголовок .exe- или .dll-файла. Кроме того, объем изначально передаваемой памяти можно переопределить вызовом CreateThread или _beginthreadex. У обеих функций есть параметр, который позволяет изменять объем памяти, изначально передаваемой региону стека. Если в нем передать 0, система будет использовать значение, внедренное в РЕ-загловок. Далее я исхожу из того, что стек создается со стандартными параметрами.

На рис. 16-1 показано, как может выглядеть регион стека (зарезервированный по адресу 0x08000000) в системе с размером страниц по 4 Кб. Регион стека и вся переданная ему память имеют атрибут защиты PAGE_READWRITE.

Зарезервировав регион, система передает физическую память двум верхним его страницам. Непосредственно перед тем, как приступить к выполнению потока, система устанавливает регистр указателя стека на конец верхней страницы региона стека (адрес, очень близкий к 0x08100000). Это та

524 Часть III. Управление памятью

страница, с которой поток начнет использовать свой стек. Вторая страница сверху называется сторожевой (guard page).

Рис. 16-1. Так выглядит регион стека потока сразу после его создания

По мере разрастания дерева вызовов (одновременного обращения ко все большему числу функций) потоку, естественно, требуется и больший объем стека. Как только поток обращается к следующей странице (а она сторожевая), система уведомляется об этой попытке. Тогда система передает память еще одной странице, расположенной как раз за сторожевой. После чего флаг PAGE_GUARD, как эстафетная палочка, переходит от текущей сторожевой к той странице, которой только что передана память. Благодаря такому механизму объем памяти, занимаемой стеком, увеличивается только по необходимости. Если дерево вызовов у потока будет расти и дальше, регион стека будет выглядеть примерно так, как показано на рис. 16-2.

Допустим, стек потока практически заполнен (как на рис. 16-2) и регистр указателя стека указывает на адрес 0x08003004. Тогда, как только поток вызовет еще одну функцию, система, по идее, должна передать дополнительную физическую память. Но когда система передает память странице по адресу 0x08001000, она делает это уже по-другому. Регион стека теперь выглядит, как на рис. 16-3.

Соседние файлы в предмете Программирование на C++