Address Windowing Extensions (Windows 2000 only)

[Previous] [Next]

As days go by, applications require more and more memory. This is especially true of server applications: As an increasing number of clients make requests of the server, the server's performance diminishes. To improve performance, the server application needs to keep more of its data in RAM and reduce disk paging. Other classes of applications—such as database, engineering, and scientific—also require the ability to manipulate large blocks of storage. For all these applications, a 32-bit address space is just not enough room.

To help these applications, Windows 2000 offers a new feature called Address Windowing Extensions (AWE). Microsoft had two goals when creating AWE:

  • Allow applications to allocate RAM that is never swapped by the operating system to or from disk.
  • Allow an application to access more RAM than fits within the process's address space.

Basically, AWE provides a way for an application to allocate one or more blocks of RAM. When allocated, these blocks are not visible in the process's address space. Then, the application reserves a region of address space (using VirtualAlloc), which becomes the address window. The application then calls a function that assigns one RAM block at a time to the address window. Assigning a RAM block to the address window is extremely fast (usually on the order of a few milliseconds).

Obviously only one RAM block at a time can be accessed via a single address window. This makes your code more difficult to implement since you must explicitly call functions within your code to assign different RAM blocks to the address window as you need them.

The code below shows how to use AWE:

 // First, reserve a 1MB region for the address window ULONG_PTR ulRAMBytes = 1024 * 1024 PVOID pvWindow = VirtualAlloc(NULL, ulRAMBytes, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE); // Get the number of bytes in a page for this CPU platform SYSTEM_INFO sinf; GetSystemInfo(&sinf); // Calculate the required number of RAM pages for the // desired number of bytes ULONG_PTR ulRAMPages = ulRAMBytes / sinf.dwPageSize // Allocate array for RAM page's page frame numbers ULONG_PTR aRAMPages[ulRAMPages]; // Allocate the pages of RAM (requires Lock Pages in Memory user right) AllocateUserPhysicalPages( GetCurrentProcess(), // Allocate the storage for our process &ulRAMPages, // Input: # of RAM pages, Output: # pages allocated aRAMPages); // Output: Opaque array indicating pages allocated // Assign the RAM pages to our window MapUserPhysicalPages(pvWindow, // The address of the address window ulRAMPages, // Number of entries in array aRAMPages); // Array of RAM pages // Access the RAM pages via the pvWindow virtual address  // Free the block of RAM pages FreeUserPhysicalPages( GetCurrentProcess(), // Free the RAM allocated for our process &ulRAMPages, // Input: # of RAM pages, Output: # pages freed aRAMPages); // Input: Array indicating the RAM pages to free // Destroy the address window VirtualFree(pvWindow, 0, MEM_RELEASE); 

As you can see, using AWE is simple. Now, let me point out a few interesting things about this code.

The call to VirtualAlloc reserves a 1-MB address window. Usually, the address window is much bigger. You must select a size that is appropriate for the size of the RAM blocks your application requires. Of course, the largest contiguous free block available in your address space determines the largest window you can create. The MEM_RESERVE flag indicates that I am just reserving a region of addresses. The MEM_PHYSICAL flag indicates that this region will eventually be backed by physical RAM storage. One limitation of AWE is that all storage mapped to the address window must be readable and writable; hence PAGE_READWRITE is the only valid protection that can be passed to VirtualAlloc. In addition, you cannot use the VirtualProtect function to alter this protection.

Allocating physical RAM is simply a matter of calling AllocateUserPhysicalPages:

 BOOL AllocateUserPhysicalPages( HANDLE hProcess, PULONG_PTR pulRAMPages, PULONG_PTR aRAMPages); 

This function allocates the number of RAM pages specified in the value pointed to by the pulRAMPages parameter and assigns these pages to the process identified by the hProcess parameter.

Each page of RAM is assigned a page frame number by the operating system. As the system selects pages of RAM for the allocation, it populates the array—pointed to by the aRAMPages parameter—with each RAM page's page frame number. The page frame numbers themselves are not useful in any way to your application; you should not examine the contents of this array and you most definitely should not alter any of the values in this array. Note that you neither know which pages of RAM were allocated to this block nor should you care. When the address window shows the pages in the RAM block, they appear as a contiguous block of memory. This makes the RAM easy to use and frees you from having to understand exactly what the system is doing internally.

When the function returns, the value in pulRAMPages indicates the number of pages that the function successfully allocated. This will usually be the same value that you passed to the function, but it can also be a smaller value.

Only the owning process can use the allocated RAM pages; AWE does not allow the RAM pages to be mapped into another process's address space. Therefore, you cannot share RAM blocks between processes.

NOTE
Of course, physical RAM is a very precious resource and an application can only allocate whatever RAM has not already been dedicated. You should use AWE sparingly or your process and other processes will excessively page storage to and from disk, severely hurting overall performance. In addition, less available RAM adversely affects the system's ability to create new processes, threads, and other resources. An application can use the GlobalMemoryStatusEx function to monitor physical memory use.

To help protect the allocation of RAM, the AllocateUserPhysicalPages function requires the caller to have the "Lock Pages in Memory" user right granted and enabled or the function fails. By default, this right isn't assigned to any user or group. The right is given to the Local System account, which is typically used for services. If you want to run an interactive application that calls AllocateUserPhysicalPages, an administrator must grant you this right before you log on and run the application.

WINDOWS 2000
In Windows 2000 you can turn on the Lock Pages in Memory user right, by performing the following steps:

  1. Open the Computer Management MMC console by clicking on the Start button and then selecting the Run menu item. In the Run box, type "compmgmt.msc /a" and click on the OK button.
  2. If the Local Computer Policy item is not visible in the left-hand pane, select Add/Remove Snap-in from the Console menu. On the Standalone tab, select Computer Management (Local) from the Snap-ins Added To combo box. Now click on the Add button to display the Add Standalone Snap-in dialog box. Select Group Policy from the Available Standalone Snap-ins list, and click on the Add button. On the Select Group Policy Object dialog box leave the defaults and click on the Finish button. Click on the Close button in the Add Standalone Snap-in dialog box and click on the OK button in the Add/Remove Snap-in dialog box. The Local Computer Policy item should now be visible in the left-hand pane of the Computer Management console.
  3. In the left-hand pane of the console, double-click to expand each of the following items: Local Computer Policy, Computer Configuration, Windows Settings, Security Settings, and finally Local Policies. Select the User Rights Assignment item.
  4. In the right-hand pane, select the Lock Pages in Memory attribute.
  5. Select Security from the Action menu to display the Lock Pages in Memory dialog box. Click on the Add button. Use the Select Users or Groups dialog box to add the users and/or groups that you want to assign the Lock Pages in Memory user right. Exit each of the dialog boxes by clicking on the OK button.

User rights are granted when a user logs on. If you just granted the Lock Pages in Memory right to yourself, you must log off and log back on before it takes effect.

Now that I've created the address window and allocated a RAM block, I assign the block to the window by calling MapUserPhysicalPages:

 BOOL MapUserPhysicalPages( PVOID pvAddressWindow, ULONG_PTR ulRAMPages, PULONG_PTR aRAMPages); 

The first parameter, pvAddressWindow, indicates the virtual address of the address window and the second two parameters, ulRAMPages and aRAMPages, indicate how many and which pages of RAM to make visible in this address window. If the window is smaller than the number of pages you're attempting to map, the function fails. Microsoft's main goal for this function was to make it execute extremely fast. Typically, MapUserPhysicalPages is able to map the RAM block in just a few microseconds.

NOTE
You can also call MapUserPhysicalPages to un-assign the current RAM block by passing NULL for the aRAMPages parameter. Here is an example:

 // Un-assign the RAM block from the address window MapUserPhysicalPages(pvWindow, ulRAMPages, NULL); 

Once the RAM block has been assigned to the address window, you can easily access the RAM storage simply by referencing virtual addresses relative to the address window's base address (pvWindow in my sample code).

When you no longer need the RAM block, you should free it by calling FreeUserPhysicalPages:

 BOOL FreeUserPhysicalPages( HANDLE hProcess, PULONG_PTR pulRAMPages, PULONG_PTR aRAMPages); 

The first parameter, hProcess, indicates which process owns the RAM pages you're attempting to free. The second two parameters indicate how many pages and the page frame numbers of those pages that are to be freed. If this RAM block is currently mapped to the address window, it is unmapped and then freed.

Finally, to completely clean up, I free the address window merely by calling VirtualFree and passing the base virtual address of the window, 0 for the region's size, and MEM_RELEASE.

My simple code example creates a single address window and a single RAM block. This allows my application access to RAM that will not be swapped to or from disk. However, an application can create several address windows and can allocate several RAM blocks. These RAM blocks can be assigned to any of the address windows but the system does not allow a single RAM block to appear in two address windows simultaneously.

64-bit Windows 2000 fully supports AWE; porting a 32-bit application that uses AWE is easy and straightforward. However, AWE is less useful for a 64-bit application since a process's address space is so large. AWE is still useful because it allows the application to allocate physical RAM that is not swapped to or from disk.

The AWE Sample Application

The AWE application ("15 AWE.exe"), listed in Figure 15-3, demonstrates how to create multiple address windows and how to assign different storage blocks to these windows. The source code and resource files for the application are in the 15-AWE directory on the companion CD-ROM. When you start the program, it internally creates two address window regions and allocates two RAM blocks.

Initially, the first RAM block is populated with the string "Text in Storage 0" and the second RAM block is populated with the string "Text in Storage 1." Then the first RAM block is assigned to the first address window and the second RAM block is assigned to the second address window. The application's window reflects this.

click to view at full size.

Using this window, you can perform some experiments. First, you assign RAM blocks to address windows using each address window's combo box. The combo box also offers a No Storage option that unmaps any storage from the address window. Second, editing the text updates the RAM block currently selected in the address window.

If you attempt to assign the same RAM block to the two address windows simultaneously, the following message box appears since AWE doesn't support this.

click to view at full size.

The source code for this sample application is clear-cut. To make working with AWE easier, I created three C++ classes contained in the AddrWindows.h file. The first class, CSystemInfo, is a very simple wrapper around the GetSystemInfo function. The other two classes each create an instance of the CSystemInfo class.

The second C++ class, CAddrWindow, encapsulates an address window. Basically, the Create method reserves an address window, the Destroy method destroys the address window, the UnmapStorage method unmaps any RAM block currently assigned to the address window, and the PVOID cast operator method simply returns the virtual address of the address window.

The third C++ class, CAddrWindowStorage, encapsulates a RAM block that may be assigned to a CAddrWindow object. The Allocate method enables the Lock Pages in Memory user right, attempts to allocate the RAM block, and then disables the user right. The Free method frees the RAM block. The HowManyPagesAllocated method returns the number of pages successfully allocated. The MapStorage and UnmapStorage methods map and unmap the RAM block to or from a CAddrWindow object.

Using these C++ classes made implementing the sample application much easier. The sample application creates two CAddrWindow objects and two CAddrWindowStorage objects. The rest of the code is just a matter of calling the correct method for the proper object at the right time.

Figure 15-3. The AWE application

AWE.cpp

 /****************************************************************************** Module: AWE.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <Windowsx.h> #include <tchar.h> #include "AddrWindow.h" #include "Resource.h" /////////////////////////////////////////////////////////////////////////////// CAddrWindow g_aw[2]; // 2 memory address windows CAddrWindowStorage g_aws[2]; // 2 storage blocks const ULONG_PTR g_nChars = 1024; // 1024 character buffers /////////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_AWE); // Create the 2 memory address windows chVERIFY(g_aw[0].Create(g_nChars * sizeof(TCHAR))); chVERIFY(g_aw[1].Create(g_nChars * sizeof(TCHAR))); // Create the 2 storage blocks if (!g_aws[0].Allocate(g_nChars * sizeof(TCHAR))) { chFAIL("Failed to allocate RAM.\nMost likely reason: " "you are not granted the Lock Pages in Memory user right."); } chVERIFY(g_aws[1].Allocate(g_nChars * sizeof(TCHAR))); // Put some default text in the 1st storage block g_aws[0].MapStorage(g_aw[0]); lstrcpy((PSTR) (PVOID) g_aw[0], TEXT("Text in Storage 0")); // Put some default text in the 2nd storage block g_aws[1].MapStorage(g_aw[0]); lstrcpy((PSTR) (PVOID) g_aw[0], 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 { 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) ? (PCSTR) (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, (PSTR) (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); } /////////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { chWindows2000Required(); DialogBox(hinstExe, MAKEINTRESOURCE(IDD_AWE), NULL, Dlg_Proc); return(0); } //////////////////////////////// End of File ////////////////////////////////// 

AWE.rc

 //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_AWE DIALOG DISCARDABLE 0, 0, 288, 45 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Address Windowing Extensions" FONT 8, "MS Sans Serif" BEGIN LTEXT "Window 0:",IDC_STATIC,4,6,35,8 COMBOBOX IDC_WINDOW0STORAGE,44,4,80,58,CBS_DROPDOWNLIST | WS_TABSTOP EDITTEXT IDC_WINDOW0TEXT,132,4,152,14,ES_AUTOHSCROLL LTEXT "Window 1:",IDC_STATIC,4,28,35,8 COMBOBOX IDC_WINDOW1STORAGE,44,25,80,58,CBS_DROPDOWNLIST | WS_TABSTOP EDITTEXT IDC_WINDOW1TEXT,132,25,152,14,ES_AUTOHSCROLL END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_AWE, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 281 TOPMARGIN, 7 BOTTOMMARGIN, 38 END END #endif // APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_AWE ICON DISCARDABLE "AWE.ico" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED 

AddrWindow.h

 /****************************************************************************** Module: AddrWindow.h Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #pragma once /////////////////////////////////////////////////////////////////////////////// #include "..\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 fOk = TRUE; if (m_pvWindow != NULL) { // Destroy address window region fOk = VirtualFree(m_pvWindow, 0, MEM_RELEASE); m_pvWindow = NULL; } return(fOk); } 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();  // Cleanup this object's existing address window       // Calculate number of pages from number of bytes       m_ulPages = (ulBytes + sm_sinf.dwPageSize) / sm_sinf.dwPageSize;       // Allocate array of page frame numbers       m_pulUserPfnArray = (PULONG_PTR)           HeapAlloc(GetProcessHeap(), 0, m_ulPages * sizeof(ULONG_PTR));       BOOL fOk = (m_pulUserPfnArray != NULL);       if (fOk) {          // The "Lock Pages in Memory" privilege must be enabled          EnablePrivilege(SE_LOCK_MEMORY_NAME, TRUE);          fOk = AllocateUserPhysicalPages(GetCurrentProcess(),              &m_ulPages, m_pulUserPfnArray);          EnablePrivilege(SE_LOCK_MEMORY_NAME, FALSE);       }       return(fOk);    }    BOOL Free() {       BOOL fOk = TRUE;       if (m_pulUserPfnArray != NULL) {          fOk = FreeUserPhysicalPages(GetCurrentProcess(),              &m_ulPages, m_pulUserPfnArray);          if (fOk) {             // Free the array of page frame numbers             HeapFree(GetProcessHeap(), 0, m_pulUserPfnArray);             m_ulPages = 0;              m_pulUserPfnArray = NULL;           }       }       return(fOk);    }    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 fEnable = TRUE) {       BOOL fOk = FALSE;    // Assume function fails       HANDLE hToken;       // Try to open this process's access token       if (OpenProcessToken(GetCurrentProcess(),           TOKEN_ADJUST_PRIVILEGES, &hToken)) {          // Attempt to modify the "Lock pages in Memory" user right          TOKEN_PRIVILEGES tp = { 1 };          LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid);          tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;          AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);          fOk = (GetLastError() == ERROR_SUCCESS);          CloseHandle(hToken);       }       return(fOk);    } 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 ////////////////////////////////// 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net