본문 바로가기

High Level Technique/Window System

메모리 관리

메모리 관리




Virtual Memory Control


Reserve는 예약, Commit은 할당, Free는 할당되지 않음을 말한다. Windows 시스템에서 부여할 수 있도록 정의한 페이지의 상태를 말하는 것이다.


페이지의 총 개수는 가상 메모리 크기 / 페이지 하나당 크기 = 페이지의 개수 로 나타낼 수 있다.


Commit은 물리 메모리에 할당이 이루어진 부분들이다. malloc을 통해 메모리 할당하는 것 또한 메모리의 페이지는 Commit 상태가 된다.


반대로 할당되지 않은 부분은 free 상태이다.


Commit과 Free를 이용해서 상태를 나타낼 수 있지만 Reserve 상태라는 것을 두고 예약 공간을 만들어 둔다.


메모리의 사용량이 늘어남에 따라서 Commit 상태의 페이지 수를 늘릴 수 있는 것이다.


Windows에서는 메모리가 지나치게 조각나는 것을 막기 위하여 더 큰 크기로 할당을 한다. 이를 Allocation Granularity Boundary라 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <Windows.h>
 
int main(void)
{
    SYSTEM_INFO si;
    DWORD allocGranularity;
    DWORD pageSize;
 
    GetSystemInfo(&si);
    pageSize = si.dwPageSize;
 
    allocGranularity = si.dwAllocationGranularity;
 
    printf("Page Size: %u kb \n", pageSize / 1024);
    printf("Allocation granularity: %u kb \n", allocGranularity/1024);
 
    return 0;
}
cs







VirtualAlloc, VirtualFree 함수


페이지의 상태를 Reserve와 Commit 상태로 만드는 함수가 VirtualAlloc함수 이다. VirtualFree는 VirtualAlloc 함수가 정해 놓은 상태를 되돌리는 역할을 한다.




Dynamic Array Design


1. 시스템의 페이지 사이즈와 Allocation Granularity Boundary 값을 얻어온다. 할당하고자 하는 메모리의 위치에 직접적으로 관여하지 않겠다면 페이지 사이즈만 얻어와도 된다.


2. 메모리를 예약한다. 예약을 할 때에는 필요하다고 예상되는 최대의 크기로 예약을 한다.


3. 필요한 만큼의 메모리를 물리 메모리에 할당한다. 필요에 따라 점진적으로 할당의 크기를 증가시킨다.


4. 할당한 메모리를 반환한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <stdio.h>
#include <Windows.h>
 
#define max_page 10
 
int* nextPageAddr;
DWORD pageCnt = 0;
DWORD pageSize;
int PageFaultExceptionFilter(DWORD);
 
int main(void)
{
    LPVOID baseAddr;
    int* lpPtr;
    SYSTEM_INFO sSysInfo;
 
    GetSystemInfo(&sSysInfo);
    pageSize = sSysInfo.dwPageSize;
 
    baseAddr = VirtualAlloc(NULL, max_page*pageSize, MEM_RESERVE, PAGE_NOACCESS);
    if (baseAddr == NULL)
    {
        printf("VirtaulAlloc reserve failed ! \n");
    }
 
    lpPtr = (int*)baseAddr;
    nextPageAddr = (int*)baseAddr;
 
    for (int i = 0; i < (max_page*pageSize)/sizeof(int); i++)
    {
        __try
        {
            lpPtr[i] = i;
        }
        __except (PageFaultExceptionFilter(GetExceptionCode()))
        {
            ExitProcess(GetLastError());
        }
    }
 
    //for (int i = 0; i < (max_page*pageSize) / sizeof(int); i++)
    //{
    //    printf("%d \n", lpPtr[i]);
    //}
 
    BOOL isSuccess = VirtualFree(baseAddr, 0, MEM_RELEASE);
 
    if (isSuccess)
    {
        printf("Release Succeeded! \n");
    }
    else
    {
        printf("Release failed! \n");
    }
 
    return 0;
}
 
int PageFaultExceptionFilter(DWORD exptCode)
{
    if (exptCode != EXCEPTION_ACCESS_VIOLATION)
    {
        printf("Exception code: %d \n", exptCode);
        return EXCEPTION_EXECUTE_HANDLER;
    }
 
    printf("Exception is a page fault \n");
 
    if (pageCnt >= max_page)
    {
        printf("Exceptino: out of pages \n");
        return EXCEPTION_EXECUTE_HANDLER;
    }
 
    LPVOID lpvResult = VirtualAlloc((LPVOID)nextPageAddr, pageSize, MEM_COMMIT, PAGE_READWRITE);
 
    if (lpvResult == NULL)
    {
        printf("VirtualAlloc failed \n");
        return EXCEPTION_EXECUTE_HANDLER;
    }
    else
    {
        printf("Allocating another page. \n");
    }
 
    pageCnt++;
    nextPageAddr += pageSize / sizeof(int);
 
    return EXCEPTION_CONTINUE_EXECUTION;
}
 
cs



힙 컨트롤


동적할당을 이용하면 Heap 영역에 값이 쌓이게 됩니다. 어떤 프로그램이 리스트 자료구조를 사용한다면 중간에 값이 필요 없어진 경우 삭제를 해야합니다. 이러한 값을 한번에 지울 수는 없죠.


관련되어있는 모든 데이터를 삭제해야 합니다. 이렇게 정상적으로 삭제 되지 않은 데이터들이 있다면 메모리 유출이 일어날 수 있습니다.




가령 A라는 사람의 데이터와 B라는 사람의 데이터가 각각 독립적인 영역에 존재한다면 A라는 사람의 데이터만 지워버리면 문제될 것이 없습니다.


프로세스를 생성할 때 생성되는 Heap은 디폴트 힙 영역에 메모리를 할당하게 됩니다. 기본적으로 할당되는 힙이기 때문에 Process Heap이라고도 합니다.



디폴트 힙 또한 크기를 수정할 수 있습니다. 속성 - 링커 - 시스템에서 값을 수정하면 됩니다.





Memory Mapped File (MMF)


MMF는 파일을 메모리에 매핑시킨다는 말입니다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
 
int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hFile = CreateFile(_T("data.dat"), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("Could not open file. \n");
    }
 
    TCHAR fileData[] = _T("Best test string~ ^^");
    DWORD numofByteWritten = 0;
    WriteFile(hFile, fileData, sizeof(fileData), &numofByteWritten, NULL);
 
    HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 00, NULL);
    if (hMapFile == NULL)
    {
        printf("Could not create map of file. \n");
    }
 
    TCHAR* pWrite = (TCHAR*)MapViewOfFile(hMapFile, FILE_MAP_READ, 000);
    if (pWrite == NULL)
    {
        printf("Could not map view of file. \n");
    }
 
    _tprintf(_T("Strig in file: %s \n"), pWrite);
 
    UnmapViewOfFile(pWrite);
    CloseHandle(hMapFile);
 
    CloseHandle(hFile);
 
    printf("End \n");
 
    return 0;
}
cs



8라인 CreateFile 함수를 이용해서 파일을 생성합니다 읽기/쓰기가 모두 가능하도록 접근권한을 지정했습니다. 

19라인 CreateFileMapping 함수를 이용해서 파일 연결 오브젝트를 생성합니다. GENERIC_READ 속성을 가지고 잇기 때문에 파일 연결 오브젝트에 PAGE_READONLY 속성을 지정할 수 있습니다.

25라인 가상 메모리로 연결을 합니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
 
void SortIntData(int* pSortArr, int num);
 
int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hFile = CreateFile(_T("data.dat"), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("Could not open file. \n");
    }
 
    HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 01024*10, NULL);
    if (hMapFile == NULL)
    {
        printf("Could not create map of file. \n");
    }
 
    int* pWrite = (int*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 000);
    if (pWrite == NULL)
    {
        printf("Could not map view of file. \n");
    }
 
    pWrite[0= 1, pWrite[1= 3, pWrite[2= 0;
    pWrite[3= 2, pWrite[4= 4, pWrite[5= 5;
    pWrite[6= 8, pWrite[7= 6, pWrite[8= 7;
 
    SortIntData(pWrite, 9);
 
    _tprintf(_T("%d %d %d \n"), pWrite[0], pWrite[1], pWrite[2]);
    _tprintf(_T("%d %d %d \n"), pWrite[3], pWrite[4], pWrite[5]);
    _tprintf(_T("%d %d %d \n"), pWrite[6], pWrite[7], pWrite[8]);
 
    UnmapViewOfFile(pWrite);
    CloseHandle(hMapFile);
 
    CloseHandle(hFile);
 
    printf("End \n");
 
    return 0;
}
 
void SortIntData(int* pSortArr, int num)
{
    int temp;
 
    for (int i = 0; i < num - 1; i++)
    {
        for (int j = 1; j < num; j++)
        {
            if (pSortArr[j - 1> pSortArr[j])
            {
                temp = pSortArr[j - 1];
                pSortArr[j - 1= pSortArr[j];
                pSortArr[j] = temp;
            }
        }
    }
 
}
cs


위 코드는 파일에 저장된 값을 정렬하는 프로그램입니다. 


17라인의 CreateFileMapping에서 0, 1024*10 으로 인자가 들어가는데 파일 연결의 최대 메모리(상위 4바이트), 파일 연결의 최대 메모리(하위 4바이트)를 나타냅니다.


위 두 값을 모두 0으로 넘겨주면 연결하고자 하는 파일의 크기로 그 값이 결정됩니다.


파일이 생성이 되었을 텐데 해당 파일의 용량을 살펴보면 10KB로 되어있는 것을 볼 수 있습니다. 값을 몇개 넣어지도 않았는데도 말이죠.


앞서 1024*10으로 설정을 해주었기 때문입니다. 





Copy-On-Write (COW)


Copy-On-Write는 쓸때 복사를 하란 의미이다. 


어떤 데이터가 쓰레드별로 모두 가지고 있는 것 보다 하나의 데이터를 공유하도록 하고 변경되는 순간 해당 쓰레드에게 할당한 후 복사본을 변경하게 하는 것을 말합니다.

메모리를 최적화 하는 방법입니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
 
int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hFile = CreateFile(_T("data.dat"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("Could not open file. \n");
    }
 
    HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 01024*10, NULL);
    if (hMapFile == NULL)
    {
        printf("Could not create map of file. \n");
    }
 
    int* pWrite = (int*)MapViewOfFile(hMapFile, FILE_MAP_COPY, 000);
    if (pWrite == NULL)
    {
        printf("Could not map view of file. \n");
    }
 
    pWrite[0= 1, pWrite[1= 3, pWrite[2= 0;
    pWrite[3= 2, pWrite[4= 4, pWrite[5= 5;
    pWrite[6= 8, pWrite[7= 6, pWrite[8= 7;
 
    _tprintf(_T("%d %d %d \n"), pWrite[0], pWrite[1], pWrite[2]);
    _tprintf(_T("%d %d %d \n"), pWrite[3], pWrite[4], pWrite[5]);
    _tprintf(_T("%d %d %d \n"), pWrite[6], pWrite[7], pWrite[8]);
 
    UnmapViewOfFile(pWrite);
    CloseHandle(hMapFile);
 
    CloseHandle(hFile);
 
    printf("End \n");
 
    return 0;
}
cs


위 소스코드를 실행하면 실제 파일에는 반영되지 않은 것을 볼 수 있다. COW 기반으로 동작했다는 것을 보여주는 결과입니다. MMF 기반에서 데이터를 변경하려는 순간에 파일의 복사본을 만들어서 작업했기 때문에 원본은 그대로 보전된 것입니다.

'High Level Technique > Window System' 카테고리의 다른 글

비동기 I/O (Asynchronous I/O)  (0) 2017.01.09
SEH (Structured Exception Handling)  (0) 2017.01.09
캐쉬와 가상메모리  (0) 2017.01.05
쓰레드 풀 (Thread Pool)  (0) 2017.01.04
생산자/소비자 모델  (1) 2017.01.04