'Memory'에 해당되는 글 2건

Memory Technical Articles
Managing Memory-Mapped Files in Win32
 

Randy Kath
Microsoft Developer Network Technology Group

Created: February 9, 1993


Abstract

Determining which function or set of functions to use for managing memory in your Win32™ application is difficult without a solid understanding of how each group of functions works and the overall impact they each have on the Microsoft® Windows NT™ operating system. In an effort to simplify these decisions, this technical article focuses on the use of the memory-mapped file functions in Win32: the functions that are available, the way they are used, and the impact their use has on operating system resources. The following topics are discussed in this article:

  • Introduction to managing memory in Windows™ operating systems
  • What are memory-mapped files?
  • How are memory-mapped files implemented?
  • Sharing memory with memory-mapped files
  • Using memory-mapped file functions

In addition to this technical article, a sample application called ProcessWalker is included on the Microsoft Developer Network CD. This sample application is useful for exploring the behavior of memory-mapped files in a process, and it provides several useful implementation examples.

Introduction

This is one of three related technical articles—"Managing Virtual Memory in Win32," "Managing Memory-Mapped Files in Win32," and the upcoming "Managing Heap Memory in Win32"—that explain how to manage memory in applications for the Win32™ programming interface. In each article, this introduction identifies the basic memory components in the Win32 programming model and indicates which article to reference for specific areas of interest.

The first version of the Microsoft® Windows™ operating system introduced a method of managing dynamic memory based on a single global heap, which all applications and the system share, and multiple, private local heaps, one for each application. Local and global memory management functions were also provided, offering extended features for this new memory management system. More recently, the Microsoft C run-time (CRT) libraries were modified to include capabilities for managing these heaps in Windows using native CRT functions such as malloc and free. Consequently, developers are now left with a choice—learn the new application programming interface (API) provided as part of Windows version 3.1 or stick to the portable, and typically familiar, CRT functions for managing memory in applications written for Windows 3.1.

With the addition of the Win32 API, the number of choices increases. Win32 offers three additional groups of functions for managing memory in applications: memory-mapped file functions, heap memory functions, and virtual-memory functions. These new functions do not replace the existing memory management functions found in Windows version 3.1; rather, they provide new features that generally make life easier for developers when writing the memory management portions of their applications for Win32.

Figure 1. The Win32 API provides different levels of memory management for versatility in application programming.

In all, six sets of memory management functions exist in Win32, as shown in Figure 1, all of which were designed to be used independently of one another. So which set of functions should you use? The answer to this question depends greatly on two things: the type of memory management you want and how the functions relevant to it are implemented in the operating system. In other words, are you building a large database application where you plan to manipulate subsets of a large memory structure? Or maybe you're planning some simple dynamic memory structures, such as linked lists or binary trees? In both cases, you need to know which functions offer the features best suited to your intention and exactly how much of a resource hit occurs when using each function.

Table 1 categorizes the memory management function groups in Win32 and indicates which of the three technical articles in this series describes each group's behavior. Each technical article emphasizes the impact these functions have on the system by describing the behavior of the system in response to using the functions.

Table 1. Various Memory Management Functions Available in Win32

Memory set System resource affected Related technical article
Virtual memory functions A process's virtual address space
System pagefile
System memory
Hard disk space
"Managing Virtual Memory in Win32"
Memory-mapped file functions A process's virtual address space
System pagefile
Standard file I/O
System memory
Hard disk space
"Managing Memory-Mapped Files in Win32"
Heap memory functions A process's virtual address space
System memory
Process heap resource structure
"Managing Heap Memory in Win32"
Global heap memory functions A process's heap resource structure "Managing Heap Memory in Win32"
Local heap memory functions A process's heap resource structure "Managing Heap Memory in Win32"
C run-time reference library A process's heap resource structure "Managing Heap Memory in Win32"

Each technical article discusses issues surrounding the use of Win32-specific functions. For a better understanding of how the Windows NT™ operating system manages system memory, see "The Virtual-Memory Manager in Windows NT" on the Microsoft Developer Network CD (Technical Articles, Win32 and Windows NT Articles).

What Are Memory-Mapped Files?

Memory-mapped files (MMFs) offer a unique memory management feature that allows applications to access files on disk in the same way they access dynamic memory—through pointers. With this capability you can map a view of all or part of a file on disk to a specific range of addresses within your process's address space. And once that is done, accessing the content of a memory-mapped file is as simple as dereferencing a pointer in the designated range of addresses. So, writing data to a file can be as simple as assigning a value to a dereferenced pointer as in:

*pMem = 23;

Similarly, reading from a specific location within the file is simply:

nTokenLen = *pMem;

In the above examples, the pointer pMem represents an arbitrary address in the range of addresses that have been mapped to a view of a file. Each time the address is referenced (that is, each time the pointer is dereferenced), the memory-mapped file is the actual memory being addressed.

Note   While memory-mapped files offer a way to read and write directly to a file at specific locations, the actual action of reading/writing to the disk is handled at a lower level. Consequently, data is not actually transferred at the time the above instructions are executed. Instead, much of the file input/output (I/O) is cached to improve general system performance. You can override this behavior and force the system to perform disk transactions immediately by using the memory-mapped file function FlushViewOfFile explained later.

What Do Memory-Mapped Files Have to Offer?

One advantage to using MMF I/O is that the system performs all data transfers for it in 4K pages of data. Internally all pages of memory are managed by the virtual-memory manager (VMM). It decides when a page should be paged to disk, which pages are to be freed for use by other applications, and how many pages each application can have out of the entire allotment of physical memory. Since the VMM performs all disk I/O in the same manner—reading or writing memory one page at a time—it has been optimized to make it as fast as possible. Limiting the disk read and write instructions to sequences of 4K pages means that several smaller reads or writes are effectively cached into one larger operation, reducing the number of times the hard disk read/write head moves. Reading and writing pages of memory at a time is sometimes referred to as paging and is common to virtual-memory management operating systems.

Another advantage to using MMF I/O is that all of the actual I/O interaction now occurs in RAM in the form of standard memory addressing. Meanwhile, disk paging occurs periodically in the background, transparent to the application. While no gain in performance is observed when using MMFs for simply reading a file into RAM, other disk transactions can benefit immensely. Say, for example, an application implements a flat-file database file structure, where the database consists of hundreds of sequential records. Accessing a record within the file is simply a matter of determining the record's location (a byte offset within the file) and reading the data from the file. Then, for every update, the record must be written to the file in order to save the change. For larger records, it may be advantageous to read only part of the record into memory at a time as needed. Unfortunately, though, each time a new part of the record is needed, another file read is required. The MMF approach works a little differently. When the record is first accessed, the entire 4K page(s) of memory containing the record is read into memory. All subsequent accesses to that record deal directly with the page(s) of memory in RAM. No disk I/O is required or enforced until the file is later closed or flushed.

Note   During normal system paging operations, memory-mapped files can be updated periodically. If the system needs a page of memory that is occupied by a page representing a memory-mapped file, it may free the page for use by another application. If the page was dirty at the time it was needed, the act of writing the data to disk will automatically update the file at that time. (A dirty page is a page of data that has been written to, but not saved to, disk; for more information on types of virtual-memory pages, see "The Virtual-Memory Manager in Windows NT" on the Developer Network CD.)

The flat-file database application example is useful in pointing out another advantage of using memory-mapped files. MMFs provide a mechanism to map portions of a file into memory as needed. This means that applications now have a way of getting to a small segment of data in an extremely large file without having to read the entire file into memory first. Using the above example of a large flat-file database, consider a database file housing 1,000,000 records of 125 bytes each. The file size necessary to store this database would be 1,000,000 * 125 = 125,000,000 bytes. To read a file that large would require an extremely large amount of memory. With MMFs, the entire file can be opened (but at this point no memory is required for reading the file) and a view (portion) of the file can be mapped to a range of addresses. Then, as mentioned above, each page in the view is read into memory only when addresses within the page are accessed.

How Are They Implemented?

Since Windows NT is a page-based virtual-memory system, memory-mapped files represent little more than an extension of an existing, internal memory management component. Essentially all applications in Windows NT are represented in their entirety by one or more files on disk and a subset of those files resident in random access memory (RAM) at any given time. For example, each application has an executable file that represents pages of executable code and resources for the application. These pages are swapped into and out of RAM, as they are needed, by the operating system. When a page of memory is no longer needed, the operating system relinquishes control over the page on behalf of the application that owns it and frees it for use by another. When that page becomes needed again, it is re-read from the executable file on disk. This is called backing the memory with a file, in this case, the executable file. Similarly, when a process starts, pages of memory are used to store static and dynamic data for that application. Once committed, these pages are backed by the system pagefile, similar to the way the executable file is used to back the pages of code. Figure 2 is a graphical representation of how pages of code and data are backed on the hard disk.

Figure 2. Memory used to represent pages of code in processes for Windows NT are backed directly by the application's executable module while memory used for pages of data are backed by the system pagefile.

Treating both code and data in the same manner paves the way for propagating this functionality to a level where applications can use it, too—which is what Win32 does via memory-mapped files.

Shared Memory in Windows NT

Both code and data are treated the same way in Windows NT—both are represented by pages of memory and both have their pages backed by a file on disk. The only real difference is the file by which they are backed—code by the executable image and data by the system pagefile. Because of this, memory-mapped files are also able to provide a mechanism for sharing data between processes. By extending the memory-mapped file capability to include portions of the system pagefile, applications are able to share data that is backed by the pagefile. Shown in Figure 3, each application simply maps a view of the same portion of the pagefile, making the same pages of memory available to each application.

Figure 3. Processes share memory by mapping independent views of a common region in the system pagefile.

Windows NT's tight security system prevents processes from directly sharing information among each other, but MMFs provide a mechanism that works with the security system. In order for one process to share data with another via MMFs, each process must have common access to the file. This is achieved by giving the MMF object a name that both processes use to open the file.

Internally, a shared section of the pagefile translates into pages of memory that are addressable by more than one process. To do this, Windows NT uses an internal resource called a prototype page-table entry (PPTE). PPTEs enable more than one process to address the same physical page of memory. A PPTE is a system resource, so their availability and security is controlled by the system alone. This way processes can share data and still exist on a secure operating system. Figure 4 indicates how PPTEs are used in Windows NT's virtual addressing scheme.

Figure 4. Prototype page-table entries are the mechanism that permits pages of memory to be shared among processes.

One of the best ways to use an MMF for sharing data is to use it in a DLL (dynamic-link library). The PortTool application serves as a useful illustration. PortTool uses a DLL to provide its porting functionality and relies on the main application for the user interface. The reason for this is simple: Other applications can then also use the DLL functionality. That is, other editors that are programmable can import the porting functionality. Because it is entirely feasible for PortTool to be running while another editor that imports the PortTool DLL is also running, it is best to economize system resources as much as possible between the applications. PortTool does this by using an MMF for sharing the porting information with both processes. Otherwise, both applications would be required to load their own set of porting information while running at the same time, a waste of system resources. The PortTool code demonstrates sharing memory via an MMF in a DLL.


Using Memory-Mapped File Functions

Memory-mapped file functions can be thought of as second cousins to the virtual-memory management functions in Win32. Like the virtual-memory functions, these functions directly affect a process's address space and pages of physical memory. No overhead is required to manage the file views, other than the basic virtual-memory management that exists for all processes. These functions deal in reserved pages of memory and committed addresses in a process. The entire set of memory-mapped file functions are:

  • CreateFileMapping
  • OpenFileMapping
  • MapViewOfFile
  • MapViewOfFileEx
  • UnmapViewOfFile
  • FlushViewOfFile
  • CloseHandle

Each of these functions is individually discussed below, along with code examples that demonstrate their use.

Creating a File Mapping

To use a memory-mapped file, you start by creating a memory-mapped file object. The act of creating an MMF object has very little impact on system resources. It does not affect your process's address space, and no virtual memory is allocated for the object (other than for the internal resources that are necessary in representing the object). One exception, however, is that, if the MMF object represents shared memory, an adequate portion of the system pagefile is reserved for use by the MMF during the creation of the object.

The CreateFileMapping function is used to create the file-mapping object as demonstrated in the example listed below, a portion of PMEM.C, the source module from the ProcessWalker sample application.

case IDM_MMFCREATENEW:
    {
    char    szTmpFile[256];

    /* Create temporary file for mapping. */
    GetTempPath (256, szTmpFile);
    GetTempFileName (szTmpFile,
                     "PW",
                     0,
                     MMFiles[wParam-IDM_MMFCREATE].szMMFile);

    /* If file created, continue to map file. */
    if ((MMFiles[wParam-IDM_MMFCREATE].hFile =
           CreateFile (MMFiles[wParam-IDM_MMFCREATE].szMMFile,
                       GENERIC_WRITE | GENERIC_READ,
                       FILE_SHARE_WRITE,
                       NULL,
                       CREATE_ALWAYS,
                       FILE_ATTRIBUTE_TEMPORARY,
                       NULL)) != (HANDLE)INVALID_HANDLE_VALUE)
        goto MAP_FILE;
    }
    break;

case IDM_MMFCREATEEXIST:
    {
    char   szFilePath[MAX_PATH];
    OFSTRUCT   of;

    /* Get existing filename for mapfile. */
    *szFilePath = 0;
    if (!GetFileName (hWnd, szFilePath, "*"))
        break;

    /* If file opened, continue to map file. */
    if ((MMFiles[wParam-IDM_MMFCREATE].hFile =
            (HANDLE)OpenFile (szFilePath, &of, OF_READWRITE)) !=
                (HANDLE)HFILE_ERROR)
        goto MAP_FILE;
    }
    break;

case IDM_MMFCREATE:
    /* Associate shared memory file handle value. */
    MMFiles[wParam-IDM_MMFCREATE].hFile = (HANDLE)0xffffffff;

MAP_FILE:
    /* Create 20MB file mapping. */
    if (!(MMFiles[wParam-IDM_MMFCREATE].hMMFile =
        CreateFileMapping (MMFiles[wParam-IDM_MMFCREATE].hFile,
                           NULL,
                           PAGE_READWRITE,
                           0,
                           0x01400000,
                           NULL)))
        {
        ReportError (hWnd);
        if (MMFiles[wParam-IDM_MMFCREATE].hFile)
            {
            CloseHandle (MMFiles[wParam-IDM_MMFCREATE].hFile);
            MMFiles[wParam-IDM_MMFCREATE].hFile = NULL;
            }
        }
    break; /* from IDM_MMFCREATE */

In the sample code above, three cases are demonstrated. They represent creating a memory-mapped file by first creating a temporary disk file, creating a memory-mapped file from an existing file, and creating a memory-mapped file out of part of the system pagefile. In case IDM_MMFCREATENEW, a temporary file is created first, before the memory-mapped file. For case IDM_MMFCREATEEXIST, the File Open dialog is used to retrieve a filename, and that file is then opened before the memory-mapped file is created. In the third case, IDM_MMFCREATE, the memory-mapped file is created either using the system pagefile or using one of the standard files created in the two earlier cases.

Notice that the CreateFileMapping function need only be called once for all three different cases. The first parameter to the CreateFileMapping function, hFile, is used to supply the handle to the file that is to be memory-mapped. If the system pagefile is to be used, the value 0xFFFFFFFF must be specified instead. In the above examples, a structure is used to represent both the standard file and memory-mapped file information. In the example above, the hMMFile field in the structure MMFiles[wParam-IDM_MMFCREATE] is either 0xFFFFFFFF (its default value), or it is the value of the file handle retrieved in either of the earlier cases.

In all three cases, the memory-mapped file is specified to be 20 MB (0x01400000) in size, regardless of the size of any files created or opened for mapping. The fourth and fifth parameters, dwMaximumSizeHigh and dwMaximumSizeLow, are used to indicate the size of the file mapping. If these parameters indicate a specific size for the memory-mapped file when memory mapping a file other than the pagefile, the file on disk is fitted to this new size—whether larger or smaller makes no difference. As an alternative, when memory mapping a file on disk, you can set the size parameters to 0. In this case, the memory-mapped file will be the same size as the original disk file. When mapping a section of the pagefile, you must specify the size of the memory-mapped file.

The second parameter to the CreateFileMapping function, lpsa, is used to supply a pointer to a SECURITY_ATTRIBUTES structure. Since memory-mapped files are an object, they have the same security attributes that can be applied to every other object. A NULL value indicates that no security attributes are relevant to your use of the memory-mapped file.

The third parameter, fdwProtect, is used to indicate the type of protection to place on the entire memory-mapped file. You can use this parameter to protect the memory-mapped file from writes by specifying PAGE_READONLY or to permit read and write access with PAGE_READWRITE.

One other parameter of interest is the lpszMapName parameter, which can be used to give the MMF object a name. In order to open a handle to an existing file-mapping object, the object must be named. All that is required of the name is a simple string that is not already being used to identify another object in the system.

Obtaining a File-Mapping Object Handle

In order to map a view of a memory-mapped file, all you need is a valid handle to the MMF object. You can obtain a valid handle in one of several ways: by creating the object as described above, by opening the object with the OpenFileMapping function, by inheriting the object handle, or by duplicating the handle.

Opening a memory-mapped file object

To open a file-mapping object, the object must have been given a name during the creation of the object. A name uniquely identifies the object to this and other processes that wish to share the MMF object. The following portion of code from PORT.C shows how to open a file-mapping object by name.

/* Load name for file-mapping object. */
LoadString (hDLL, IDS_MAPFILENAME, szMapFileName, MAX_PATH);

/* After first process initializes, port data. */
if ((hMMFile = OpenFileMapping (FILE_MAP_WRITE, 
                                FALSE, 
                                szMapFileName)))
    /* Exit now since initialization was already performed by 
       another process. */
     return TRUE;

/* Retrieve path and file for ini file. */
if (!GetIniFile (hDLL, szIniFilePath))
    return FALSE;

/* Test for ini file existence and get length of file. */
if ((int)(hFile = (HANDLE)OpenFile (szIniFilePath, 
                                    &of, 
                                    OF_READ)) == -1)
    return FALSE;

else
    {
    nFileSize = GetFileSize (hFile, NULL);
    CloseHandle (hFile);
    }

/* Allocate a segment of the swap file for shared memory 2*Size 
   of ini file. */
if (!(hMMFile = CreateFileMapping ((HANDLE)0xFFFFFFFF,
                                    NULL,
                                    PAGE_READWRITE,
                                    0,
                                    nFileSize * 2,
                                    szMapFileName)))
    return FALSE;

The OpenFileMapping function requires only three arguments, the most important of these being the name of the object. As shown in the example, the name is simply a unique string. If the string is not unique to the system, the MMF object will not be created. Once the object exists, however, the name is guaranteed for the life of the object.

Also, note in the above example that the MMF object is opened first, possibly before the object has been created. This logic relies on the fact that, if the object does not already exist, the OpenFileMapping function will fail. This is useful in a DLL where the DLL's initialization code is called repeatedly, once for every process that attaches to it.

The sample from PORT.C above occurs in the DLL's initialization code that is called every time a DLL gets attached to another process. The first time it is called, the OpenFileMapping function fails because the object does not already exist. The logic, then, continues execution until it reaches the CreateFileMapping function, and it is there that the object is first created. Immediately after initially creating the object, the PortTool code initializes the data in the file mapping by writing porting-specific information to the memory-mapped file. To do this, the memory-mapped file is created with PAGE_READWRITE protection. All subsequent calls to the DLL's initialization function result in the OpenFileMapping function successfully returning a valid object handle. This way the DLL does not need to keep track of which process is the first to attach to the DLL.

Note that for every process that attaches to the DLL, the object name is retrieved from the same source—a string from the DLL's resource string table. Since the DLL is able to retrieve the object name from its own resource string table, the name is global to all processes, yet no process is actually aware of the name used. The DLL is able to effectively encapsulate this functionality while at the same time providing the benefit of shared memory to each process that attaches to the DLL.

The PortTool example presents a useful context for sharing memory. Yet, keep in mind that any file on disk could have been used in the same way. If an application were to implement some database services to several other applications, it could set up memory-mapped files using basic disk files, instead of the pagefile, and share that information in the same way. And as the first code listing illustrates, a temporary file could be used to share data instead of the pagefile.

Inheriting and duplicating memory-mapped file object handles

Ordinarily, for two processes to share a memory-mapped file, they must both be able to identify it by name. An exception to this is child processes, which can inherit their parent's handles. Most objects in Win32 can be explicitly targeted for inheritance or not. (Some objects are not inheritable, such as GDI object handles.) When creating an MMF object, a Boolean field in the optional SECURITY_ATTRIBUTES structure can be used to designate whether the handle is to be inheritable or not. If the MMF object handle is designated as inheritable, any child processes of the process that created the object can access the object through the same handle as their parent.

Literally, this means the child process can access the object by supplying the same handle value as the parent. Communicating that handle to the child process is another concern. The child process is still another process after all, having its own address space, so the handle variable itself is not transferable. Either some interprocess communication (IPC) mechanism or the command line can be used to communicate handle values to child processes.

Further, the DuplicateHandle function is provided to offer more control as to when handles can be inherited and not. This function can be used to create a duplicate handle of the original and can be used to change the inheritance state of the handle. An application can invoke this function to change an MMF object handle state to inheritable before passing the handle along to a child process, or it can do the opposite—it can take an inheritable handle and preserve it from being inherited.

Viewing Part of a Memory-Mapped File

Once obtained, the handle to the memory-mapped file object is used to map views of the file to your process's address space. Views can be mapped and unmapped at will while the MMF object exists. When a view of the file is mapped, system resources are finally allocated. A contiguous range of addresses, large enough to span the size of the file view, are now committed in your process's address space. Yet, even though the addresses have been committed for the file view, physical pages of memory are still only committed on a demand basis when using the memory. So, the only way to allocate a page of physical memory for a committed page of addresses in your memory-mapped file view is to generate a page fault for that page. This is done automatically the first time you read or write to any address in the page of memory.

To map a view of a memory-mapped file, use either the MapViewOfFile or the MapViewOfFileEx function. With both of these functions, a handle to a memory-mapped file object is a required parameter. The following example shows how the PortTool sample application implements this function.

/* Map a view of this file for writing. */
lpMMFile = (char *)MapViewOfFile (hMMFile, 
                                  FILE_MAP_WRITE, 
                                  0, 
                                  0, 
                                  0);

In this example, the entire file is mapped, so the final three parameters are less meaningful. The first parameter specifies the file-mapping object handle. The second parameter indicates the access mode for the view of the file. This can be FILE_MAP_READ, FILE_MAP_WRITE, or FILE_MAP_ALL_ACCESS, provided the protection on the file-mapping object permits it. If the object is created with PAGE_READWRITE protection, all of these access types are available. If, on the other hand, the file is created with PAGE_READONLY protection, the only access type available is FILE_MAP_READ. This allows the object creator control over how the object can be viewed.

The second and third parameters are used to indicate the low and high halves, respectively, of a 64-bit offset into the memory-mapped file. This offset from the start of the memory-mapped file is where the view is to begin. The final parameter indicates how much of the file is to be viewed. This parameter can be set to 0 to indicate that the entire file is to be mapped. In that case, the 64-bit offset value is ignored.

The function returns a pointer to the location in the process's address space where the file view has been mapped. This is an arbitrary location in your process, depending on where the contiguous range of addresses are available. If you want to map the file view to a specific set of addresses in your process, the MapViewOfFileEx function provides this capability. This function simply adds an additional parameter, lpvBase, to indicate the location in your process to map the view. The return value to MapViewOfFileEx is the same value as lpvBase if the function is successful; otherwise, it is NULL. Similarly, for MapViewOfFile the return value is NULL if the function fails.

Multiple views of the same file-mapping object can coexist and overlap each other as shown in Figure 5.

Figure 5. Memory-mapped file objects permit multiple, overlapped views of the file from one or more processes at the same time.

Notice that multiple views of a memory-mapped file can overlap, regardless of what process maps them. In a single process with overlapping views, you simply end up with two or more virtual addresses in a process that refer to the same location in physical memory. So, it's possible to have several PTEs referencing the same page frame. Remember, each page of a shared memory-mapped file is represented by only one physical page of memory. To view that page of memory, a process needs a page directory entry and page-table entry to reference the page frame.

There are two ways in which needing only one physical page of memory for a shared page benefits applications in the system. First, there is an obvious savings of resources because both processes share both the physical page of memory and the page of hard disk storage used to back the memory-mapped file. Second, there is only one set of data, so all views are always coherent with one another. This means that changes made to a page in the memory-mapped file via one process's view are automatically reflected in a common view of the memory-mapped file in another process. Essentially, Windows NT is not required to do any special bookkeeping to ensure the integrity of data to both applications.

Unmapping a View of a Memory-Mapped File

Once a view of the memory-mapped file has been mapped, the view can be unmapped at any time by calling the UnmapViewOfFile function. As you can see below, there is nothing tricky about this function. Simply supply the one parameter that indicates the base address, where the view of the file begins in your process

/* Load tokens for APIS section. */
LoadString (hDLL, IDS_PORTAPIS, szSection, MAX_PATH);
if (!LoadSection (szIniFilePath, 
                  szSection, 
                  PT_APIS, 
                  &nOffset, 
                  lpMMFile))
        {
        /* Clean up memory-mapped file. */
        UnmapViewOfFile (lpMMFile);
        CloseHandle (hMMFile);
        return FALSE;
        }

As mentioned above, you can have multiple views of the same memory-mapped file, and they can overlap. But what about mapping two identical views of the same memory-mapped file? After learning how to unmap a view of a file, you could come to the conclusion that it would not be possible to have two identical views in a single process because their base address would be the same, and you wouldn't be able to distinguish between them. This is not true. Remember that the base address returned by either the MapViewOfFile or the MapViewOfFileEx function is not the base address of the file view. Rather, it is the base address in your process where the view begins. So mapping two identical views of the same memory-mapped file will produce two views having different base addresses, but nonetheless identical views of the same portion of the memory-mapped file.

The point of this little exercise is to emphasize that every view of a single memory-mapped file object is always mapped to a unique range of addresses in the process. The base address will be different for each view. For that reason the base address of a mapped view is all that is required to unmap the view.

Flushing Views of Files

An important feature for memory-mapped files is the ability to write any changes to disk immediately if necessary. This feature is provided through the FlushViewOfFile function. Changes made to a memory-mapped file through a view of the file, other than the system pagefile, are automatically written to disk when the view is unmapped or when the file-mapping object is deleted. Yet, if an application needs to force the changes to be written immediately, FlushViewOfFile can be used for that purpose.

  /* Force changes to disk immediately. */
FlushViewOfFile (lpMMFile, nMMFileSize);

The example listed above flushes an entire file view to disk. In doing so, the system only writes the dirty pages to disk. Since the Windows NT virtual-memory manager automatically tracks changes made to pages, it is a simple matter for it to enumerate all dirty pages in a range of addresses, writing them to disk. The range of addresses is formed by taking the base address of the file view supplied by the first parameter to the FlushViewOfFile function as the starting point and extending to the size supplied by the second parameter, cbFlush. The only requirement is that the range be within the bounds of a single file view.

Releasing a Memory-Mapped File

Like most other objects in the Win32 subsystem, a memory-mapped file object is closed by calling the CloseHandle function. It is not necessary to unmap all views of the memory-mapped file before closing the object. As mentioned above, dirty pages are written to disk before the object is freed. To close a memory-mapped file, call the CloseHandle function, which supplies the memory-mapped file object handle for the function parameter.

/* Close memory-mapped file. */
CloseHandle (hMMFile);

It is worth noting that closing a memory-mapped file does nothing more than free the object. If the memory-mapped file represents a file on disk, the file must still be closed using standard file I/O functions. Also, if you create a temporary file explicitly for use as a memory-mapped file as in the initial ProcessWalker example, you are responsible for removing the temporary file yourself. To illustrate what the entire cleanup process may look like, consider the following example from the ProcessWalker sample application.

case IDM_MMFFREE:
case IDM_MMFFREENEW:
case IDM_MMFFREEEXIST:
    {
    HCURSOR    hOldCursor;
    OFSTRUCT   of;

    /* Put hourglass cursor up. */
    hOldCursor = (HCURSOR)SetClassLong (hWnd, GCL_HCURSOR, 0);
    SetCursor (LoadCursor (0, IDC_WAIT));

    /* Release memory-mapped file and associated file if any. */
    CloseHandle (MMFiles[wParam-IDM_MMFFREE].hMMFile);
    MMFiles[wParam-IDM_MMFFREE].hMMFile = NULL;

    if (MMFiles[wParam-IDM_MMFFREE].hFile)
        {
        CloseHandle (MMFiles[wParam-IDM_MMFFREE].hFile);
        MMFiles[wParam-IDM_MMFFREE].hFile = NULL;
        }

    /* If temporary file, delete here. */
    if (wParam == IDM_MMFFREENEW)
        {
        OpenFile (MMFiles[wParam-IDM_MMFFREE].szMMFile, 
                  &of, 
                  OF_DELETE);
        *(MMFiles[wParam-IDM_MMFFREE].szMMFile) = 0;
        }

    /* Replace wait cursor with old cursor. */
    SetClassLong (hWnd, GCL_HCURSOR, (LONG)hOldCursor);
    SetCursor (hOldCursor);
    }
    break;

In this example, the memory-mapped file can be one of three types: the system pagefile, a temporary file, or an existing file on disk. If the file is the system pagefile, the memory-mapped file object is simply closed, and no additional cleanup is necessary. If the memory-mapped file is mapped from an existing file, that file is closed right after closing the memory-mapped file. If the memory-mapped file is a mapping of a temporary file, it is no longer needed and is deleted using standard file I/O immediately after closing the temporary file handle, which cannot occur until after closing the memory-mapped file object handle.

Conclusion

Memory-mapped files provide unique methods for managing memory in the Win32 application programming interface. They permit an application to map its virtual address space directly to a file on disk. Once a file has been memory-mapped, accessing its content is reduced to dereferencing a pointer.

A memory-mapped file can also be mapped by more than one application simultaneously. This represents the only mechanism for two or more processes to directly share data in Windows NT. With memory-mapped files, processes can map a common file or portion of a file to unique locations in their own address space. This technique preserves the integrity of private address spaces for all processes in Windows NT.

Memory-mapped files are also useful for manipulating large files. Since creating a memory mapping file consumes few physical resources, extremely large files can be opened by a process and have little impact on the system. Then, smaller portions of the file called "views" can be mapped into the process's address space just before performing I/O.

There are many techniques for managing memory in applications for Win32. Whether you need the benefits of memory sharing or simply wish to manage virtual memory backed by a file on disk, memory-mapped file functions offer the support you need.

신고

'[04] programming' 카테고리의 다른 글

cmd 명령어로 ip 수정 및 네트워크 설정  (0) 2009.02.25
SQL Error Code  (0) 2009.02.19
L-Value and R-Value Expressions(C/C++ Language Reference) from MSDN  (0) 2009.01.05
Debugging Lab Seminar movie  (0) 2008.11.27
Thread 스케줄링  (0) 2008.10.02
Memory Map(From MSDN)  (0) 2008.09.23
Process & Activity & Task  (0) 2008.08.04
RMI-IIOP  (0) 2008.07.22
Calling Convention  (1) 2008.06.16
VARIANT  (0) 2008.06.16
VARENUM  (0) 2008.06.16
블로그 이미지

Moonistar moonistar

Memory

[04] programming 2008.06.13 12:42

논리적 메모리

1.        프로그램이 생성할 때부터 종료할 때까지 메모리에 상주하는 부분
A.       main() 함수와 함수코드(Function)
B.       전역변수(Global Variable)
C.       정적변수(Static Variable)
2.        프로그램 실행 도중에 생성 및 소멸되는 부분
A.       지역변수(Local Variable)
B.       매개변수(Parameter)
C.       Heap 메모리에 생성된 데이터
 
 
함수 또는 클래스
스택 영역(Stack)
지역변수, 매개변수와 같이 쓰고 지우는 일이 빈번한 데이터는 스택영역을 사용한다. 스레드 당 1개씩 생성되며, 기본 크기는 1MB이다. 용량이 작아서 이 용량을 초과할 경우 Stack Overflow라는 에러 메시지가 발생한다. 링킹시 옵션으로 /STACK:reserve [.commit] 형태로 지정을 하면, 그 내용이 실행파일(EXE) 초반부에 기록되고, 프로그램이 실행될 때 운영체제가 이를 참조하여 스택이 사용할 메모리 영역을 할당한다.
데이터영역(Data)
Static 변수
코드 내에서 static 키워드로 생성된 데이터로써, 프로그램 생성시 할당된다.
전역변수(Global)
함수 블록 내에 포함되지 않은 변수로서, 프로그램 생성시 할당된다.
동적할당(Heap)
힙 메모리 함수에 의해 생성되는 데이터로 개발자의 필요에 따라 할당, 해제 할 수 있다. 프로그램이 실행되면 기본적으로 1MB의 크기의 힙 메모리 영역을 할당한다.
코드 영역(Code)
함수 코드가 이 영역에 저장되며, 함수 코드는 프로그램이 실행될 때 변경되면 안 되므로 읽기 전용이다.
 


Windows
메모리 관리


32비트 윈도 운영체제에서 하나의 프로그램은 4GB의 메모리 영역을 가질 수 있다. 그러나 4GB를 모두 어플리케이션 마음대로 사용할 수 있는 것은 아니고, 상위 2GB만 프로그램이 사용할 수 있도록 하였고, 하위 2GB Windows가 실행된 프로그램을 관리하기 위한 코드가 적재된다. 위의 스택, 코드 등의 영역은 하위 2GB에 포함된다.

 

가상 메모리상태 (Virtual
 


Memory Status)



1.
       
램에 맵핑된 상태, 하드 디스크에 맵핑된 상태

2.
       
사용이 예약되어 읽거나 쓸 수 있는 상태

3.
       
초기 상태
 
등급
내용
Committed
물리적 메모리에 맵핑된 상태의 메모리 영역이다. Commit 상태의 메모리 영역은 읽거나 쓸 수 있다. VirtualAlloc()을 통하여 Commit 상태로 변경할 수 있으며, Commit 상태를 해제하여 Reserved 또는 Commit 상태로 바꿀 수 있다. WIN16 API LocalAlloc()을 이용하여 Commit 상태로 변경할 수도 있다.
Reserved
특정 크기가 메모리 영역의 사용을 예약해 놓은 상태이다. , 현재는 사용하지 않지만 앞으로 필요하게 될 부분이므로 다른 함수에 의해서 메모리가 할당될 때, 이 부분은 사용하지 말라는 뜻이다. 이 상태에서는 읽거나 쓸 수 없다. 왜냐하면, 물리적 메모리와 맵핑되지 않은 상태이기 때문이다. 사용하기 위해서는 commit 상태로 되어야 한다. 이때 사용되는 함수가 VirtualAlloc() 이다. Reserved 상태를 해제하려면, VirtualFree() 를 사용한다. Reserved 영역을 해제하면 Free 상태가 되고, 프로그램의 다른 부분에 의해 자유롭게 사용이 가능하게 된다.
Free
최초 가상 메모리가 생성될 때, 모든 가상 메모리 영역은 Free 상태에 놓이게 되는데, 읽거나 쓸 수 없는 빈 영역이라고 생각하면 된다. 이 영역을 Reserved 또는 Commit 상태로 변경할 수 있다. 물론 물리적 메모리와는 맵핑 되지 않은 상태이다.
 



가상
메모리 접근 등급



(Memory Access
 


Protection)



l
       
접근불가 PAGE_NOACCESS

l
       
읽기전용 PAGE_READONLY

l
       
읽기/쓰기 PAGE_READWRITE
 
등급
내용
PAGE_NOACCESS
접근이 금지된 상태
PAGE_READONLY
읽기 전용 상태. 중요한 데이터의 경우 덮어쓰기 등의 경우로 데이터를 잃어버리는 경우를 막기 위하여 사용된다.
PAGE_READWRITE
읽거나 쓸 수 있는 상태. 가장 일반적인 형태로 Commit된 메모리 페이지에 대하여 모든 권한을 부여한다.
 
Windows 운영체제는 메모리에 대하여 접근 제한 속성을 설정할 수 있도록 API를 제공한다. 이러한 내용들은 파일에 대한 접근 등급과도 유사하며, 커널 오브젝트의 특징인 보안 속성과도 유사하다. 속성을 설정할 때는 메모리를 할당할 때 사용하는 VirtualAlloc()을 사용하고 생성된 이후에 VirtualQuery()를 통해 할당된 메모리 영역의 속성을 확인할 수 있다.

 

32비트 윈도우 가상 메모리




 구조(4GB)



Windows
운영체제가 제공하는 4GB의 메모리 영역을 차지하는 스택, , 코드 영역은 매우 작은 공간에 불과하며, 이외에도 많은 영역들이 존재한다.
 
0x00000000
0x00000FFF
NULL 값 할당 영역(4KB)
Private
2GB
0x00001000
0x003FFFFF
도스 및 16비트 어플리케이션 영역(4KB)
0x00400000
0x7FFFFFFF
프로세스 영역(사용자 영역)
User-Mode(1.99GB)
0x80000000
0xC0000000
공유 메모리 영역(메모리 맵 파일 영역)
Shared Memory-Mapped File(1GB)
Shared
2GB
0xFFFFFFFF
커널 영역
Kernel-Mode(1GB)
Windows 98/Me 가상 메모리 구조
 
 
0x00000000
0x0000FFFF
0x00001000
NULL 값 할당 영역(4KB)
Private
2GB
0xBFFEFFFF
0xBFFF0000
0xBFFFFFFF
0xC0000000
프로세스 영역(사용자 영역)
User-Mode(1.99GB)
 
Off-Limit 영역(64KB)
0xFFFFFFFF
커널 영역
Kernel-Mode(2GB)
Shared
2GB
Windows 2000 가상 메모리 구조
 
Private: 어플리케이션만의 독립적인 사용영역.
Shared: 커널에 의해서 공유되어 사용되는 물리적 메모리 영역을 맵핑해 놓은 부분. , 여러 프로세스가 공유해서 쓰는 커널 부분 또는 메모리 맵 파일 부분의 물리적 메모리 영역을 맵핑시켜 놓은 부분이다.
 
1.        NULL 포인터 영역
이 영역의 값은 모두 0이고 변경 불가능이다. , 운영체제에 의해 NULL 값으로 이미 정해진 절대 접근 금지 영역으로서 이 영역을 읽거나 쓰려고 시도할 경우 Access Violation 에러를 발생시키고, 프로세스는 종료된다. 시스템의 안정성 확보를 위해서 Windows가 할당해 놓은 구간이다.
 
2.        16비트 영역(MS-DOS Windows 3.x 어플리케이션 영역)
MS-DOS
Windows 3.1 운영체제는 16비트 기반이다. Window95 이후의 버전은 32비트로 업그레이드 되었지만, 기존과의 호환성을 위하여 16비트 또는 도스용 어플리케이션 및 디바이스를 사용할 수 있도록 일정 영역을 할당하고 있는데, NT 계열에서는 사용하지 않고 클라이언트 버전인 Windows 95/98/Me에서 사용된다. 32비트 기반의 어플리케이션이 이 영역을 사용하려고 하면 Access Violation 에러를 발생시키고, 프로세스는 운영체제에 의해 종료된다. 32비트, 64비트 Windows 운영체제인 NT4.0 이후의 버전에는 존재하지 않는 영역이다.
 
3.        프로세스 영역(어플리케이션 영역)
응용프로그램이 사용하는 영역. 응용프로그램 코드가 상주한다. 스택, , 코드 영역 등이 여기에 해당한다. 가상메모리, 힙 메모리 할당 등이 모두 프로세스 영역에서 이루어진다.
 
4.        중간 경계 영역
어플리케이션 영역과 공유 메모리 영역 중간의 64KB 크기 메모리 영역을 정하였다. 이 영역은 Private영역과 Shared영역을 구분하기 위한 완충지대를 둔 것이다. NULL 포인터 영역과 같이 접근을 시도할 경우 Access Violation을 일으킨다.
 
5.        공유 메모리 영역(메모리 맵 파일 영역)
Windows98
에서 사용되는 영역으로 운영체제, 즉 시스템이 사용하는 데이터 중 프로세스들과 공유되는 데이터들이 저장되는 영역이다. 또한 메모리 맵 파일(Memory Mapped File)을 사용하여 하드 디스크의 대용량 스트림 데이터를 사용할 때 Windows에 의해 사용되며, 프로세스간의 통신에도 사용된다
.
Windows 2000
이후에는 이 영역이 사라지고, 커널 모드가 최대 2GB를 사용하도록 구분되어 있다. 다시 말하면, Windows2000에서는 더욱 프로세스의 독립성과 안정성을 강조하고 있다고 볼 수 있다.
 
6.        커널 영역
운영체제 시스템 코드가 로드 되는 부분이 커널 모드 영역이다. 예를 들면, 스레드 스케줄링, 메모리 관리, 파일 시스템 코드, 네트워크 관련 코드, 그 밖의 기타 디바이스 드라이버 등이 로드 된다. 이 영역은 시스템 내의 모든 프로세스에게 공유된 메모리이다. 이것은 물리적 메모리에 한 번 로드 되며, 모든 프로세스가 공통으로 사용한다는 뜻인데, 프로세스가 이 영역을 직접 접근하는 것은 막고 있다. API함수를 통하여 얻은 핸들을 통해서만 이 영역을 사용할 수 있다.

 

WIN32 메모리 관련 API
 


오브젝트

 
WIN32 Application (32비트 윈도우 프로그램)
 
 
 
Local, Global Memory API
CRT Memory Function
 
WIN32 SubSystem
 
Heap Memory API
WIN32 Mapped File API
Virtual Memory API
Windows Virtual Memory Manager
Kernel
 
1.        Local, Global Memory API : LocalAlloc(), GlobalAlloc() 등의win16 API
2.        Heap Memory API : HeapAlloc(), HeapFree() 등의 힙 메모리 관리API
3.        Virtual Memory API : Windows 메모리 관리의 기본인 가상 메모리API
4.        Memory Mapped File API : 메모리 맵 파일을 다루기 위한API
 
메모리 맵 파일 API를 제외하고 모든 API는 가상 메모리 API를 이용하고 있다. 상위의 힙 메모리, 로컬/글로벌 메모리 API는 결국에 가상 메모리 API를 사용하여 구현되었다는 것이다. Windows 운영체제가 가상 메모리 API를 기반으로 만들어졌으니 당연한 일일 것이다.
 
 
구분
내용
Virtual Memory
대용량 객체 또는 구조체 배열 관리에 용이
Heap Memory
작은 용량의 많은 데이터 관리에 용이
Memory Mapped File
파일과 같은 대용량 스트림(stream) 데이터, 시스템 내 프로세스간의 공유 데이터 관리에 용이
 
가상 메모리는 Windows 가 메모리를 관리하는 기본 원칙과 같은 것으로, 메모리 맵 파일을 제외한 모든 메모리 관련 API 또는 오브젝트들은 가상 메모리 관련 함수들을 이용하여 특정 용도에 맞도록 보다 쉽게 구현된 것들이다. 따라서, 결국에는 가상 메모리 API 함수를 통하여 메모리를 사용하거나 관리한다고 이해하면 된다. 각각의 구체적인 특징과 사용방법을 살펴보기로 하자.


 

가상 메모리(Virtual
 


Memory)



가상 메모리 API를 사용하여 메모리를 사용할 때는 시스템에 따른 페이지 크기로만 가능하므로, 불과 몇 바이트의 작은 크기를 사용할 때는 효과적이지 못하다. 따라서, 대용량의 구조체 또는 객체의 배열을 사용하고자 할 때 사용된다. 배열 사용이 가능한 이유는 연속된 메모리 영역을 할당할 수 있기 때문이다.
 

virtualAlloc()



1.
       
페이지 단위로 일정 크기의 메모리 영역을 할당한다.

2.
       
Free 상태의 페이지를 Reserved 상태로 바꾼다.

3.
       
Reserved 상태의 페이지를 Committed 상태로 바꾼다.

4.
       
Free 상태의 페이지를 Reserved 또는 Committed 상태로 바꾼다.

5.
       
PAGE_READWRITE, PAGE_READONLY, PAGE_NOACCESS 등의 접근 속성을 가진다.
 
LPVOID VirtualAlloc(
LPVOID lpAddress,                    // Resion to reserve or commit
SIZE_T dwSize,                          // 메모리 영역의 크기
DWORD flAllocationType,           // 할당 유형 선택
DWORD flProtect                        // 접근 속성 선택
);
 

virtualFree()


1.
       
Commit된 페이지를 Decommit시켜, Reserve된 상태로 바꾼다
.
Decommit
로 인하여 맵핑된 물리적 메모리는 해제되며, 해당 물리적 메모리를 다른 프로세스가 사용 가능하도록 한다. Decommit라는 뜻은 페이지와 물리적 메모리와의 맵핑 상태를 해제하여 다른 프로세스 등이 사용할 수 있도록 한다는 뜻.

 

2.
       
Commit가 아닌 Reserve 또는 Free 상태의 메모리는 참조할 수 없다
.
만일 참조하려 한다면, Access Violation 예외를 일으킨다. 메모리를 직접적으로 참조하거나 사용하려면, Commit 상태를 유지해야 한다.
 
BOOL VirtualFree(
LPVOID lpAddress,                    // committed 페이지 영역의 시작 주소
DWORD dwSize,                         // 메모리 영역의 크기
DWORD dwFreeType                 // Free, Reserved 상태로의 변경 중 선택
}
 

기타 함수


1.
       
VirtualProtect / VirtualProtectEx : 메모리 페이지의 접근 등급을 변경하는API

2.
       
VirtualLock / VirtualUnlock : 특정 영역을 잠근다. 특정영역에 대한 사용을 중지 시키는API

3.
       
VirtualQuery / VirtualQueryEx : 특정 영역의 메모리 정보를 얻는 API. 메모리 영역의 접근 등급, 상태 등을 구조체 MEMORY_BASIC_INFORMATION 를 통하여 알 수 있다.


 

메모리



Windows
는 기본적으로 페이지 단위의 가상 메모리 관리 기법으로 메모리를 관리한다. 이러한 Windows의 가상 메모리 관리 방법과 동일한 수준의 관리를 개발자가 할 수 있도록 가상 메모리 API를 제공한다. 따라서, 페이지 단위의 메모리 할당, commit, 해제 등의 작업을 개발자가 해야 한다.
32바이트를 사용하기 위해 4098바이트 페이지 전체를 물리적 메모리에 맵핑하여 사용해야 하므로, 작은 크기의 데이터에 사용하는 것은 부적절하다.
또한 가상 메모리 API는 각각의 페이지들을 관리하기 위한 페이지 디렉토리(Page Directory) 라는 것이 있고, 이는 64KB 크기의 실제의 물리적 메모리를 소모한다.
작은 크기의 데이터를 사용하는데 있어서 가상 메모리의 이와 같은 단점을 보완한 힙 메모리 API를 제공하고 있다.
 
힙 메모리 할당은 힙 메모리 API를 지칭하기도 하고,
malloc()계열의 C runtime library,
WIN16 GlobalAlloc(), LocalAlloc(),
OLE2 IMalloc 인터페이스,
힙 메모리 API를 총괄해서 부르기도 한다.
다음은 힙 메모리 오브젝트를 이용한 방법.
 
힙 메모리는 커널 오브젝트인 힙 오브젝트를 이용한다. 힙 오브젝트는 크기가 작은 메모리 영역을 효율적으로 사용할 수 있도록 한다. 그리고 가상 메모리 API에서는 페이지 단위로의 메모리 영역을 할당하고 해제하는 등의 작업도 맡아서 한다.
 
1.        장점
A.       페이지 단위의 메모리 할당(Reserve, Commit) 작업이 불필요하다.
B.       힙 메모리 영역의 크기가 자동으로 증가한다.
 
2.        단점
A.       속도가 느리다
힙 메모리 영역에서의 메모리 할당과 해제 작업은 다중 스레드 환경에서 동기화로 이루어진다. 어느 한 시점에서 힙 메모리를 할당하는 작업은 하나의 스레드에게만 허용되는데, 동시에 이 작업을 요청한 다른 스레드는 대기하므로 속도가 느려진다.
B.       메모리 영역을 제어할 수 없다.
C.       다른 메모리 영역으로의 침범이 우려된다.
 
프로세스가 생성될 때, 운영체제는 프로세스에게 기본적으로 1MB의 힙 메모리 영역을 할당한다. new, malloc을 이용한 메모리 사용은 이 영역에서 이루어진다. GetProcessHeap() 으로 이미 생성된 기본 힙 메모리 오브젝트의 핸들을 리턴할 수 있다.
 

새로운 메모리 오브젝트 생성

 
HANDLE HeapCreate(
DWORD flOptions,                      // 힙 할당 옵션
SIZE_T dwInitialSize,                  // 최초 힙 메모리 영역 크기
SIZE_T dwMaximumSize            // 최대 힙 메모리 영역 크기
);
 

프로세스 기본 메모리 오브젝트 참조

 
HANDLE GetProcessHeap(VOID);
// HeapCreate 하지 않고 기본 힙 메모리 오브젝트 사용시
 

현재 생성된 다수의 메모리 오브젝트 참조

 
DWORD GetProcessHeaps(
DWORD NumberOfHeaps,           // ProcessHeaps로 얻을 힙 핸들의 최대개수
PHANDLE ProcessHeaps           // 힙 핸들 데이터 배열 포인터
);
 

메모리 영역 할당 사용

 
LPVOID HeapAlloc(
HANDLE hHeap,            // 힙 메모리 오브젝트 핸들
DWORD dwFlags,          // 메모리 할당에 적용될 옵션
SIZE_T dwBytes           // 할당할 메모리 영역의 크기
);
 

메모리 영역 해제 오브젝트 소멸

 
BOOL HeapFree(
HANDLE hHeap,            // 힙 메모리 오브젝트 핸들
DWORD dwFlags,          // 속성 설정
LPVOID lpMem              // 할당된 메모리 영역의 포인터
);
 
BOOL HeapDestroy(
  HANDLE hHeap             // 반환할 힙 메모리 오브젝트 핸들
);
 
포인터 배열, 즉 할당된 각각의 메모리 영역을 HeapFree() 로 모두 해제하고, 마지막으로 HeapDestroy() 로 해당 힙 메모리 오브젝트를 더 이상 사용하지 않을 것을 운영체제에게 알리고 있다 (CloseHandle() 을 사용하지 않고 HeapDestroy() 를 사용했다)
할당된 메모리 영역의 사용이 끝났으면 HeapFree() 로 메모리 영역을 해제하고, 전체 힙 메모리 영역에 대한 사용을 끝내려면 힙 메모리 오브젝트 핸들을 반환하면 된다
.

HeapReAlloc()    //
메모리 영역 또는 크기를 재할당
.

HeapSize()          //
생성된 힙 메모리 영역의 크기를 확인

 

메모리 파일(Memory
 


Mapped File)




Memory Mapped File
이란 File Mapping Object에 의해 물리적 메모리에 맵핑된 상태의 파일을 말한다.
메모리 맵 파일을 이용하면 일반적인 파일 또는 파일의 일부분을 자신의 프로세스 메모리 영역에 맵핑하여 사용할 수 있으며, 맵핑된 메모리 파일의 일부 또는 전부를 뷰(View)라고 한다. 이 뷰를 참조하면 입출력 방식을 사용하지 않고도 메모리의 포인터 접근방식으로 파일을 접근할 수 있다.
디스크의 파일을 읽는 방법에는 입출력(Input/Output)을 이용하는 방법이 일반적이다. 일반적인 파일 입출력은 시스템이 수행하는 여러 가지 역할 가운데 비용이 큰 편에 속한다. 파일을 참조해야 하는 경우가 자주 발생하거나 파일의 용량 자체가 거대해서 복잡한 버퍼 등을 설계해야 할 필요가 있을 때 파일 입출력을 사용하는 것은 비효율적이다. 이에 반해 메모리 맵 파일을 생성하는 것은 물리적으로 적은 리소스를 소모하기 때문에 매우 거대한 파일을 적은 대가를 지불하고도 사용할 수 있다.
 
l        메모리 맵 파일의 용도 및 특징
A.       EXE 파일과 DLL 파일을 로드하여 프로세스를 실행할 때 시스템에 의해 사용한다.
B.       대용량의 가상 메모리 영역 할당 또는 대용량 파일의 처리에 적합하다.
C.       파일의 입출력 또는 버퍼 관리 등을 하지 않고 데이터 파일의 참조가 가능하다.
D.       동일한 메모리 맵 파일을 이용하여 프로세스간 데이터를 공유가 가능하다.
 

파일 맵핑 오브젝트 생성 – CreateFileMapping()

 
HANDLE CreateFileMapping(
HANDLE hFile,              // 파일 핸들
LPSECURITY_ATTRIBUTES lpAttributes,  // 보안 속성
DWORD flProtect,                       // 접근 속성
DWORD dwMaximumSizeHigh,    // 64비트 상위 DWORD 크기
DWORD dwMaximumSizeLow,    // 32비트 크기, 64비트 하위 DWORD 크기
LPCTSTR lpName                       // 문자열 이름
}
 

메모리 파일 참조(파일 생성) – MapViewOfFile()

 
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,                 // 파일 맵핑 오브젝트 핸들
DWORD dwDesiredAccess,                    // 접근 속성
DWORD dwFileOffsetHigh,
          // 맵핑이 시작되는 포인터의 offset High-Order DWORD
DWORD dwFileOffsetLow,
          // 맵핑이 시작되는 포인터의 offset Low-Order DWORD
SIZE_T dwNumberOfBytesToMap           // 맵핑할 바이트 수
)
 

파일 소멸 맵핑 오브젝트 변환 – UnmapViewOfFile,
 
CloseHandle


사용이 끝난 파일 뷰는 UnmapViewOfFile()을 이용하여 해제하여 메모리 맵 파일과 가상 메모리와의 맵핑 상태를 해제한다. 메모리 맵 파일을 더 이상 사용할 필요가 없을 때, 커널 오브젝트인 파일 맵핑 오브젝트를 CloseHandle()을 사용하여 핸들을 반환한다. 마지막으로 디스크의 파일을 사용한 경우에는 파일을 역시 CloseHandle()을 이용하여 운영체제에게 반환하면 모든 과정이 끝나게 된다.

 

Access Violation 방지



하기
위한 함수



특정 메모리 영역의 포인터를 안전하게 사용하는 몇 가지 WIN32 API 함수
 
 
내용
IsBadCodePtr
현재의 프로세스가 해당 주소가 가리키는 영역을 읽을 수 있는지의 여부를 확인한다.
IsBadReadPtr
현재의 프로세스가 해당 주소가 가리키는 영역을 읽을 수 있는지의 여부를 확인한다.
IsBadStringPtr
현재의 프로세스가 해당 주소가 가리키는 영역을 문자열로 읽을 수 있는지의 여부 확인 하며, 시작 포인터로부터 NULL이 나올 때까지 유효성을 검사한다.
IsBadWritePtr
현재의 프로세스가 해당 주소가 가리키는 영역에 기록할 수 있는지의 여부를 확인한다.
 
 
 
 
 
출처 : Visual C++.Net Programming Bible(삼양출판사)
신고

'[04] programming' 카테고리의 다른 글

Debugging Lab Seminar movie  (0) 2008.11.27
Thread 스케줄링  (0) 2008.10.02
Memory Map(From MSDN)  (0) 2008.09.23
Process & Activity & Task  (0) 2008.08.04
RMI-IIOP  (0) 2008.07.22
Calling Convention  (1) 2008.06.16
VARIANT  (0) 2008.06.16
VARENUM  (0) 2008.06.16
Thread 스케줄링  (0) 2008.06.13
Source Safe 연결 해제 방법  (0) 2008.06.13
Memory  (0) 2008.06.13
블로그 이미지

Moonistar moonistar

Tag Memory