'물리 메모리'에 해당되는 글 2건

  1. 2010.03.24 메모리 컨트롤
  2. 2010.03.11 라이브러리(Library)

메모리 상태 :


페이지의 개수 = 가상 메모리의 크기 / 페이지 하나당 크기

페이지 개수는 가상 메모리의 크기에 비례하며(가상 메모리는 몇 비트 환경인지에 비례 (ex. 32비트 4GB)),

모든 페이지는 Reserve, Commit, Free 세가지 중 하나의 상태를 지닌다.

Commit : 물리 메모리에 할당된 상태

Reserve : Free 와 Commit 의 중간상태이다. 해당 번지에 대해 예약을 한다.
              다른 메모리 함수가 물리 메모리에 해당 번지에 할당하지 못하도록 한다.
              하지만 물리 메모리의 소비는 발생하지 않는다.
                
Free : 물리 메모리 할당이 이뤄지지 않은 상태


메모리 할당의 시작점과 단위 확인 :

가상 메모리 시스템은 페이지 단위로 관리된다.

페이지의 중간 위치에서부터 할당을 시작할수 없으며, 페이지 크기의 배수 단위로 할당한다.

Allocation Granularity Boundary : 메모리 할당의 시작 주소가 되는 기본 단위
(참고 : 메모리가 지나치게 조각나는것과 관리의 효율성을 이유로 페이지 크기의 배수보다 더 넓은 값을 가진다)

Allocation Granularity Boundary 과 페이지 크기 확인 :

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, TCHAR *argv[])
{
          SYSTEM_INFO si;
          DWORD allocGranularity;
          DWORD pageSize;
 
          GetSystemInfo(&si);
          pageSize = si.dwPageSize;
          allocGranularity = si.dwAllocationGranularity;

          _tprintf(_T("Page Size : %u Kbyte \n"), pageSize/1024);
          _tprintf(_T("Allocation granularity : %u Kbyte \n"), allocGranularity/1024);
 
          return 0;
}



가상 메모리 컨트롤 :

페이지 상태를 RESERVE 나 COMMIT 로 변경할때 사용하는 함수

LPVOID VirtualAlloc(
          LPVOID lpAddress,       // 예약 및 할당하고자 하는 메모리의 시작 주소
                                            // (일반적 NULL 전달, RESERVED -> COMMIT 일때 해당페이지 시작 주소 지정)
          SIZE_T dwSize,                // 할당하고자 하는 메모리의 크기를 바이트 단위로 지정
                                                 // 메모리의 할당은 페이지 크기 단위로 결정
          DWORD flAllocationType,    // 메모리 할당의 타입.
                                                 // RESERVE : MEM_MRESERVE,          COMMIT : MEM_COMMIT 
          DWORD flProtect                // 페이지별 접근방식에 제한을 두는 용도
                                                 // RESERVE 상태로 변경할때는 접근을 허용하지 않는 PAGE_NOACCESS ,
                                                 // COMMIT 상태로 변경할때는 읽고/쓰기 모드인 PAGE_READWRITE 전달
);

함수 호출이 성공하면 할당이 이뤄진 메모리의 시작 주소를 리턴


할당된 페이지를 되돌리는 함수

BOOL VirtualFree(
           LPVOID lpAddress,            // 해제할 메모리 공간의 시작 주소 
           SIZE_T dwSize,                // 해제할 메모리 크기를 바이트 단위로 지정
           DWORD dwFreeType         // MEM_DECOMMIT : 해당 페이지의 상태를 RESERVE 상태로 되돌린다.
                                                  // MEM_RELEASE : 해당 페이지의 상태를 FREE 상태로 되돌린다.
                                                  //                           두번째 전달인자 dwSize 는 반드시 0 이어야 한다.
);



힙 컨트롤 :

디폴트 힙(Default Heap) :

디폴트 힙을 구성하는 페이지들은 RESERVE 상태이다. 일부는 성능을 위해 COMMIT 상태일수도 있다.

C 언어의 malloc 함수와 free 함수, C++ 언어의 new와 delete 를 사용해서 힙영역을 사용할 경우

프로세스를 생성할때 생성되는 힙, 즉 1M 바이트 크기의 디폴트 힙에 메모리를 할당한다.
(COMMIT 와 RESERVE 상태를 오간다)

프로세스에 기본적으로 할당되는 힙이며 프로세스 힙(Process Heap)라고도 한다.


디폴트 힙 컨트롤 :

디폴트 힙의 기본 크기는 1MB 이다. 링커(Linker)옵션을 통해 크기를 변경할 수 있다.

/HEAP : reserve, commit  (ex. /HEAP 0x200000, 0x10000  :  디폴트 힙의 크기 : 2MB,       COMMIT 크기 : 64KB)

힙은 동적이라 기본 크기 1MB 로 생성이 된 후 필요에 따라 그 크기가 Windows 시스템에 의해 자동으로 늘어난다.



Windows 시스템 힙(Dynamic Heap) :

Windows 시스템 함수를 통해 여러 힙을 추가로 생성할 수 있다.

가상 메모리 범위 내에서 프로세스 생성시 만들어지는 디폴트 힙 이외에 필요로 하는 힙을 얼마든지 생성할 수 있다.


힙(Dynamic Heap) 생성이 가져다 주는 이점 :

1. 메모리 단편화의 최소화에 따른 성능 향상 :



2. 동기화 문제에서 자유로워짐으로 인한 성능 향상 :

힙은 쓰레드가 공유하는 메모리 영역이다. 따라서 둘 이상의 쓰레드가 동시접근 할때 문제가 발생할수 있으므로

Windows 내부적으로 동기화처리를 해준다.(여기서 동기화는 메모리 할당과 해제)

같은 주소 번지에 둘 이상의 쓰레드가 동시에 메모리를 할당 및 해제하는 상황이 발생할 경우 메모리 오류(Corrupt)가
발생하므로 디폴트 프로세스 힙은 동기화 처리를 하는데 쓰레드마다 독립된 힙을 가지고 있다면

이러한 동기화가 필요없으므로 성능이 향상된다.


힙(Dynamic Heap) 컨트롤 :

힙을 생성하는 함수 :

HANDLE HeapCreate(
         DWORD flOptions,                 // 생성되는 힙의 특성을 부여 (0 을 전달시 가장 일반적인 힙 생성)
                                                   // HEAP_GENERATE_EXCEPTIONS : 오류 발생시 NULL이 아닌 예외 발생
                                                   // HEAP_NO_SERIALIZE : 생성된 힙의 메모리 할당과 해제에 대해
                                                   //                                   동기화 처리를 하지 않는다.
                                                   // 둘 이상의 속성을 비트 단위 연산자 OR( | )로 동시 지정 가능
         SIZE_T dwInitialSize,            // dwMaximumSize 에서 지정한 메모리 중에서
                                                   // 초기에 할당할 COMMIT 페이지를 지정한다 
         SIZE_T dwMaximumSize      // 생성되는 힙의 크기 결정.
                                                   // 지정하는 크기에 해당하는 페이지의 수만큼 RESERVE 상태가 된다.
                                                   // 0이 아닐 경우 힙은 증가가능한 메모리(Growable Heap)가 된다
);

힙을 소멸하는 함수 :

BOOL HeapDestroy(
         HANDLE hHeap                    // 반환하고자 하는 힙의 핸들
); 

힙에 메모리를 할당하는 함수 :

LPVOID HeapAlloc(
         HANDLE hHeap,               // 메모리 할당이 이뤄질 힙의 핸들
         DWORD dwFlags,             // HEAP_GENERATE_EXCEPTIONS : 오류 발생시 NULL이 아닌 예외 발생
                                               // HEAP_NO_SERIALIZE : 함수호출시 동기화 처리되지 않는다.
                                               // (HeapCreate 함수호출에서 지정했다면 중복지정할 필요는 없다)
                                               // HEAP_ZERO_MEMORY : 할당된 메모리는 0으로 초기화
                                               // 둘 이상의 속성을 비트 단위 연산자 OR( | )로 동시 지정 가능
         SIZE_T dwBytes               // 할당하고자 하는 메모리의 크기를 지정
                                               // (증가가능한 힙이 아닐 경우 최대 크기 0x7FFF8)
);

힙에 메모리를 해제하는 함수 :

BOOL HeapFree(
         HANDLE hHeap,                // 해제할 메모리를 담고 있는 힙을 지정
         DWORD dwFlags,              // HEAP_NO_SERIALIZE 가 인자로 올 수 있다,     일반적 : 0
                                                // (HeapCreate 함수호출에서 지정했다면 중복지정할 필요는 없다)
         LPVOID lpMem                 // 해제할 메모리의 시작 주소 지정
);

Posted by Dakuo

라이브러리(Library) : 여러 프로그램에서 자주 사용하는 함수와 데이터들을
                              실행이 가능한 바이너리 형태로 묶어놓은 파일을 의미한다.
(참고 : 즉, 함수와 데이터들의 정의가 컴파일된 바이너리 코드로 라이브러리에 존재한다)

(ex.

 C Run - Time Library  Characteristics
 Libcmt.lib  Multithreaded, static link
 Libcmtd.lib  Multithreaded, static link (debug)
 Msvcrt.lib  Multithreaded, dynamic link
 Msvcrtd.lib  Multithreaded, dynamic link (debug)
---d.lib : 디버그 모드로 컴파일)



Static Library : 정적 라이브러리

개념 :

헤더파일(.h)           ->
                                               ->               라이브러리 파일(.lib)
소스파일(.cpp)       ->     (Compile(컴파일))
                                                                                                    ->          실행파일(.exe)
헤더파일(.h)           ->                                                              (Link(링크))
                                               ->               오브젝트 파일(.obj)
소스파일(.cpp)       ->     (Compile(컴파일))


위와 같이 .lib 파일과 .obj 파일을 합쳐 .exe 실행파일을 만든다.

즉, 라이브러리 파일이 실행파일 안에 포함된다.


정적 라이브러리의 메모리 사용 :

                                   AAA.exe 가상 메모리                      <-정적 라이브러리 영역->
                                  BBB.exe 가상 메모리                      <-정적 라이브러리 영역->
 
동일한 정적 라이브러리를 포함하는 두 개의 프로세스 AAA와 BBB가 있다.

AAA가 실행을 하다가 멈추고 BBB를 실행할 때 메인 메모리에 BBB의 페이지가 올라가게 된다.

페이지 2,3,5 는 BBB의 2,3,5로 교체되며 정적 라이브러리 영역 8,9도 BBB의 페이지로 교체된다.

따라서 메인 메모리의 전체 AAA 프로세스 페이지가 모두 BBB 프로세스 페이지로 바뀐다.

(추가 : 직접 제작한 라이브러리 연동하기
#pragma comment(lib, "포함할 라이브러리 이름") 를 소스코드에 추가한다
라이브러리 파일은 표준 검색 경로를 통해 찾는다)



Dynamic Link Library : 동적 라이브러리

개념 :

실행파일의 일부로 포함되지 않고 독립적으로 저장되는 라이브러리.

DLL을 사용하고자 하는 프로세스는 자신의 가상 메모리 주소에 DLL을 매핑(Mapping)시키고 DLL을 사용한다.


동적 라이브러리의 메모리 사용 :

                                   AAA.exe 가상 메모리
                                    BBB.exe 가상 메모리

DLL 기반의 두개의 프로세스 AAA와 BBB 가 있다.

AAA가 실행을 하다가 멈추고 BBB를 실행할 때 메인 메모리에 BBB의 페이지가 올라가게 된다.

2,3,5 페이지가 BBB의 2,3,5 페이지로 변경된다.

이때 뒤에 8, 9 페이지는 AAA가 사용하던 페이지 그대로 존재한다.

즉, 별도의 파일로 존재하는 DLL을 AAA와 BBB가 공유하게 된다.
(참고 : 둘 이상의 프로세스가 동일한 DLL을 공유할 경우, 메인 메모리에 페이지 단위로 공유가 이뤄진다)

만약에 페이지 8, 9가 아닌, 동일한 DLL의 다른 페이지를 프로세스 BBB가 필요로 한다면 메인 메모리에

로드되있지 않으므로 새로 로드하게 된다.


DLL의 연결 방법 :

1. 암묵적 연결(Implicit Linking) :

__declspec(dllimport) : DLL로부터 제공받을(Import) 함수를 선언할 때 사용한다.

__declspec(dllexport) : 외부에 제공할(Export) 함수를 선언할 때 사용한다.

(참고 : __declspec 는 외부에 제공할 or 제공받을 함수 및 변수 선언에 사용한다.
마이크로소프트에서 제공하는 추가석인 선언문이다)

.lib 파일과 .dll 파일의 용도 :

AAAdll.cpp              ->               AAAdll.obj   
                 (Compile(컴파일))                                ->             AAAdll.exe       < --- >        AAAdll.dll
                                                AAAdll.lib     (Link(링크))                           실행중에 참조

DLL을 생성하는 과정에서 만들어진 .lib 파일은 DLL이 제공하고자 하는 함수 정보(변수나 이름정보)를 지닌다.

즉,   .lib 파일은 링커에게 AAAdll.obj에서 호출하는 함수는 AAAdll.dll 파일에 정의되어 있다고 알려준다.

.lib 파일은 링크시 필요하고 .dll 파일은 실행할 때 필요하다.

(참고 :

정적 라이브러리를 만들때와 마찬가지로 .lib 파일을 연동해야 한다.
#pragma comment(lib, "포함할 라이브러리")를 사용한다. 라이브러리 파일은 표준 검색 경로를 통해 찾는다

네임맹글링 : C++ 컴파일러는 컴파일 과정에서 네임 맹글링(Name Mangling : 이름을 엉망으로 만든다)작업을 한다.

정의된 함수 이름을 정해진 규칙에 의해 바꿔버린다. 따라서 C++ 컴파일러로 컴파일된 DLL을 C 프로그램에서

참조한다면 에러가 발생한다.(컴파일러 회사에 따라서도 네임 맹글링 규칙이 다르다)

따라서 C++ 컴파일러가 네임 맹글링을 하지 못하도록 extern "C" 키워드를 함수 선언부에 붙여준다)


2. 명시적 연결(Explicit Linking) :

소스코드 내에 DLL 연결에 대한 코드가 존재하므로 .lib 파일이 필요가 없다.

DLL                                                                      

           void DLLFunc(int val) 
        |                                 ▲ 
        |                           FreeLibrary              
        |                                 | 
LoadLibrary                          |                    
       ▼                                 |                                                                                  프로세스 가상 메모리

           void DLLFunc(int val)              

                       |
             GetProcAddress
                       |
                       ▼ 
      DLLFunc 함수 포인터 획득


HMODULE LoadLibrary(LPCTSTR lpFileName        // 프로세스 주소 공간으로 매핑시킬 DLL 이름을 지정
);

FARPROC GetProcAddress(
           HMODULE hMoudle;           사용하고자 하는 함수나 변수가 속해 있는 DLL 모듈의 핸들           
           LPCSTR lpProcName          찾고자 하는 함수의 이름을 지정
);      lpProcName 에서 지정한 함수의 함수포인터를 반환

BOOL FreeeLibrary(HMOUDLE hMoudle                 // 반환하고자 하는 DLL 모듈의 핸들
);

(참고 : 프로세스는 내부적으로 DLL의 레퍼런스 카운트(참조 횟수 : Reference Count)를 계산한다.
LoadLibrary 함수 호출 시 지정된 DLL의 레퍼런스 카운트는 1씩 증가하고, FreeLibrary 함수 호출 시
지정된 DLL의 레퍼런스 카운트는 1씩 감소한다. 레퍼런스 카운트가 0이 되면 프로세스의 가상 메모리에서 해제된다)

DLL 명시적 연결방법의 장점 :

1) DLL이 필요한 시점에서 로딩되고, 불필요해지면 반환하기 때문에 메모리가 절약된다.

2) 프로그램 실행 중에 DLL 교체 및 선택이 가능하다.

3) 필요한 순간에 하나씩 DLL을 로딩할 수 있기 때문에 실행까지 걸리는 시간이 짧고,
    DLL 로딩에 걸리는 시간을 분산한다.

(참고 : 암묵적 연결 vs 명시적 연결

명시적 연결에 성능의 장점에도 불구하고 코드의 간결성 때문에 암묵적 연결방법을 더 선호한다)


(추가 : DLL과 메모리

DLL은 물리 메모리에 한번 올라가면, 이 DLL을 참조하는 프로세스가 모두 종료될 때까지 물리 메모리에 존재한다.

(예 :

1. 프로세스 AAA가 실행된다. 이 프로세스는 AAA.dll 을 참조한다.

   따라서 AAA.dll 은 프로세스 AAA에 가상 메모리에 매핑되면서 물리 메모리에 올라간다.

2. AAA.dll 을 참조하는 프로세스 BBB가 실행된다.
 
    AAA.dll 을 프로세스 BBB에 매핑하지만 물리 메모리에는 이미 존재하므로 로드하지 않는다.

3. 프로세스 AAA가 종료되었지만 프로세스 BBB가 AAA.dll 을 참조므로 AAA.dll 은 메모리에 남아있다.

4. 프로세스 BBB가 종료되었다. AAA.dll 을 참조하는 프로세스가 하나도 없으므로 메모리에서 반환된다)

(참고 : 처음 DLL이 빌드될 때 DLL이 할당되어야 할 가상 메모리 주소가 링커(Linker)에 의해 결정도니다.

만약에 AAA.dll을 0x10000000 에 매핑하기로 했다면, 모든 프로세스에서 0x10000000 에 매핑한다.

만약에 이주소에 다른 dll이 매핑되있다면 다른 가상 메모리 주소에 매핑되며

이때는 다른 프로세스에 의해 해당 dll이 이미 메인 메모리에 로드되었더라도 또 로드되게 된다))

'Windows > _System Programming' 카테고리의 다른 글

메모리 계층(Memory Hierarchy)  (0) 2010.03.24
MMF(Memory Mapped File)  (0) 2010.03.12
라이브러리(Library)  (0) 2010.03.11
비동기 I/O 와 APC  (0) 2010.03.08
디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
Posted by Dakuo