Windows
     via
    C/C++
15장.
애플리케이션에서
가상 메모리 사용 방법

16장.
스레드 스택

                      이영권
    whiletrue0222@gmail.com
                      아꿈사
                  2012-02-11
15장. 애플리케이션에서
 가상 메모리 사용 방법

      가상 메모리 관리 함수들
      의 사용법을 알아봅니다.
가상 메모리 함수들을 사용하면
• 주소 공간 내에 직접적으로 영역을 예약
• 예약된 영역에 물리적 저장소를 커밋
• 필요한 보호 특성을 설정
1. 주소 공간 내에 영역 예약하기
LPVOID                       예약된 메모리 주소
VirtualAlloc(                가상 메모리를 예약하는 함수
   LPVOID lpAddress,         예약하고자 하는 메모리 주소
   SIZE_T dwSize,            영역의 크기
   DWORD flAllocationType,   예약할 것인지 커밋할 것인지
   DWORD flProtect           보호 특성
 );

가상 메모리를 예약하는 함수
LPVOID                       대부분의 경우 NULL값을 전달.
VirtualAlloc(                시스템이 프리 주소 영역 중 가장 적절
                             한 공간을 찾아낸다.
   LPVOID lpAddress,
   SIZE_T dwSize,            주소를 지정할 때에는
   DWORD flAllocationType,   꼭 프로세스의 유저 모드 파티션을 가리
                             키는 값을 전달해야 한다.
   DWORD flProtect
 );                          주소는 할당 단위어야 한다.
                             (13장 sec3 참조)
LPVOID                       시스템은 항 상 CPU의 페이지 크기의
VirtualAlloc(                배수로 영역을 예약
   LPVOID lpAddress,         4KB, 8KB, 16KB 페이지 크기를 사용하
   SIZE_T dwSize,            는 머신에서
   DWORD flAllocationType,   62KB를 예약하려 하면
                             64KB로 예약한다.
   DWORD flProtect
 );
LPVOID                       MEM_RESERVE:
VirtualAlloc(                • 영역 예약
                             MEM_TOP_DOWN:
   LPVOID lpAddress,         • 단편화를 피하기 위해 높은 주소 공
   SIZE_T dwSize,              간 상에 영역을 예약하기 원할 때.
   DWORD flAllocationType,
                             다른 값들은 나중에 살펴봅니다.
   DWORD flProtect
 );
LPVOID
VirtualAlloc(                13장 section6 보호특성을 참조. P501
   LPVOID lpAddress,         PAGE_NOACCESS
   SIZE_T dwSize,            PAGE_READONLY
   DWORD flAllocationType,   PAGE_READWRITE
                             …
   DWORD flProtect
 );
LPVOID                         NUMA(Non-Uniform Memory Access) 머신에서
VirtualAllocExNuma(            수행되는 경우 성능 개선을 위해 특정
   HANDLE hProcess,            노드에 탑재된 램으로부터 가상 메모리
                               를 확보할 때 사용한다.
   LPVOID lpAddress,
   SIZE_T dwSize,              참조
   DWORD flAllocationType,     14.3 NUMA 머신에서의 메모리 관리

   DWORD flProtect,
   DWORD dwPreferredNumaNode
 );
2. 예약 영역에 저장소 커밋하기
메모리에 접근하기 위해    커밋
커밋이 필요함.        할당된 물리적 저장소에
                메모리 영역을 매핑하는
커밋은 페이지 크기 단위   것.

로 수행된다.         13장 section4 물리적 저
                장소를 영역으로 커밋하
                기. p496
커밋 방법
LPVOID                       MEM_RESERVE 대신
VirtualAlloc(                MEM_COMMIT을 사용한다.
   LPVOID lpAddress,         dwSize 전달하여 전체를 커밋하지 않을
   SIZE_T dwSize,            수 있다.
   DWORD flAllocationType,
   DWORD flProtect
 );
3. 영역에 대한 예약과 저장소 커밋을
       동시에 수행하는 방법
LPVOID                       MEM_RESERVE와
VirtualAlloc(                MEM_COMMIT을 동시에 사용한다.
   LPVOID lpAddress,
   SIZE_T dwSize,
   DWORD flAllocationType,
   DWORD flProtect
 );
큰 페이지 단위(Laarge-Page Granularity)를 사용하게 되면 성능을 개
선할 수 있다.

‘큰 페이지’의 최소 크기를 얻어오는 함수
GetLargePageMinimum
                                 MEM_LARGE_PAGE를
                                 사용하면 메모리 공간을 페
VirtualAlloc을 호출할 때              이징 불가능 영역으로 설정
                                 하여 항상 렘에 유지된다.
MEM_LARGE_PAGE 플래그를 사용.          성능↑
4. 언제 물리적 저장소를 커밋하는가
            가상 메모리 기법의 유일한 문
            제점은
            언제 물리적 저장소를
            커밋할지 판단해야 한다는 것.
커밋되었는지 확인하는 방법
1. 항상 커밋해 본다.
 –   가장 쉽지만 느려진다.
2. VirtualQuery로 확인한다.
 –   1번보다 느리다.
3. 페이지 커밋 여부를 따로 기록한다.
 –   복잡한 작업이 될 수 있다.
4. 구조적 예외 처리(SEH)를 사용한다.
• 코드가 적고
• 가장 빠르다.




SEH는 23, 24, 25장에서 자세히 다뤄진다.
5. 물리적 저장소의
        디커밋과 영역 해제하기
BOOL                   해제할 때 MEM_RELEASE를 사용.
VirtualFree(           해제 시에는
   LPVOID lpAddress,   lpAddress는 시작 주소
                       dwSize는 0이어야 한다.
   SIZE_T dwSize,
   DWORD dwFreeType    예약했던 영역을 한번에 해제해야 함.

 );
BOOL                   디커밋할 때 MEM_DECOMMIT을 사용.
VirtualFree(           페이지 단위로 가능
   LPVOID lpAddress,
                       lpAddress 시작주소, dwSize 0이면
   SIZE_T dwSize,      전체를 디커밋.
   DWORD dwFreeType
                       lpAddress + dwSize가 페이지 중간쯤이면
 );                    lpAdress ~ lpAdress + dwSize까지
                       모든 페이지 디커밋
6. 보호 특성 변경하기
VirtualProtect로 변경 가능.

잘 활용하면 잠재적인 버그로부터 보호할 수 있다.

예) 메모리를 사용할 때만 PAGE_READWRITE
사용하지 않을 때 PAGE_NOACCESS로 변경해서
접근 위반 에러를 발생하게 하는 것.
7. 물리적 저장소의 내용 리셋하기
물리적 저장소 리셋:
N개의 페이지 내용이 수정되지 않았다고 시
스템에게 알려주는 것.
수행 성능을 향상 시킨다.
예) 페이지를 로드할 때
물리적 저장소가 꽉 차있고 4번 페이지를 사용할 경우.

           1   2   3              1   4   3


1. 2를 저장               2. 4를 로드


    1      2   3   4     5
예) 리셋을 사용하면
시스템은 2번이 수정되지 않았다고 알기 때문에 2를 저장 안함.

      1   2    3             1      4     3


               1. 바로 4를 로드
                                 성능 향상!

  1   2    3    4    5
주의!
                할당할 때             리셋할 때
시작주소            내림                올림
할당크기            올림                내림


위와 같이 다른 이유는 실수로 페이지를 리셋해 버리는 것을 막기 위함.

또한 MEM_RESET은 항상 단독으로 사용되어야 한다.

MemReset예제 실행하지 말자. 컴퓨터 왕 느려짐.
주소 윈도우 확장
• AWE (Address Windowing Extension)
• 32비트 윈도우에서 더 많은 메모리를 사용
  하기 위한 방법.
AWE 특징
•   디스크로 스왑되지 않는다.
•   주소 공간보다 더 큰 램에 접근할 수 있다.
•   프로세스 주소 공간에 보여지지 않는다.
•   주소 윈도우 (Address Window)를 통해 접
    근한다.
    – 코드양이 많아진다.
주의
• SQL Server 2012부터 AWE를 지원 안함.
• http://msdn.microsoft.com/ko-
  kr/library/ms143179(v=sql.110).aspx
16장. 스레드 스택
• 시스템에서 직접 주소 공간을 예약한다.
• 스레드 생성시 스택 메모리 공간
 – 기본적으로 1MB의 주소 공간을 예약
 – 두 개의 페이지만 커밋
• 스택 영역은 모두 PAGE_READWRITE 특
  성
스레드 스택의 페이지 상태
메모리 주소           페이지 상태
0x080FF000   커밋된 페이지 (최상위)
0x080FE00    커밋된 페이지 (가드 특성)
    0
0x080FD00       예약된 페이지
    0
                   …
0x08000000   예약된 페이지 (최하위)

가드 페이지에 접근하면 현재 가드페이지를 해제
다음 페이지를 가드페이지로 지정.
모두 사용하였을 경우
메모리 주소            페이지 상태
0x080FF000     커밋된 페이지 (최상위)
0x080FE00        커밋된 페이지
    0
                    …
0x08001000       커밋된 페이지
0x08000000     예약된 페이지 (최하위)
최하위 영역은 항상 예약 상태로만 두고 커밋하지 않는다.

0x08001000에 물리적 저장소를 커밋할 때
EXCEPTION_STACK_OVERFLOW 예외를 발생시킨다.
최하위 페이지를 예약 상태로만 유지하는 이유

메모리 주소            페이지 상태
0x080FF000    커밋된 페이지 (최상위)
0x080FE00        커밋된 페이지
    0
                     …
0x08001000       커밋된 페이지
0x08000000    예약된 페이지 (최하위)
0x07FFF00      다른 용도로 커밋된 페이지
    0
0x08001000 영역을 다 사용하고 0x08000000을 사용하면 접근 위반이 발생.
최하위 영역을 커밋한다면 0x07FFF000에 접근할 수 있다.
스택 크기 변경 방법
• IDE에서 변경
 – VC++ 컴파일러 /F 옵션
 – 링커 /STACK 옵션
• 코드에서 변경
 – CreateThread, _beginthreadex 호출 시 지정
1.   C/C++ 런타임 라이브러리의 스택 확인 함수

스택을 주소 공간에 할당하더라도
사용 전까지는 물리적 저장소에
커밋하지 않는다.
스택 확인 함수
오른쪽 함수는            void SomFunction(){
4KB 페이지 크기 시스템에서     int nValue[4000]; // 16,000바이트
4개의 페이지가 필요함         nValue[0] = 0; // 할당 작업 수행
                   }

스택은 2개만 커밋되어 있음

스택을 확인하는 함수가 필요.
• 스택 확인 함수는 사용되는 영역이 커밋되
  었는지 확인.
• 컴파일러는 스택 확인이 필요한 곳에 자동
  적으로 스택 확인 함수 호출 코드를 포함.
1. Summation 예제 애플리케이션
StackOverFlow 발생
시 SHE를 사용하여 우
아하게 종료한다.
감사합니다.

Windows viac cpp_15장_16장

  • 1.
    Windows via C/C++ 15장. 애플리케이션에서 가상 메모리 사용 방법 16장. 스레드 스택 이영권 whiletrue0222@gmail.com 아꿈사 2012-02-11
  • 2.
    15장. 애플리케이션에서 가상메모리 사용 방법 가상 메모리 관리 함수들 의 사용법을 알아봅니다.
  • 3.
    가상 메모리 함수들을사용하면 • 주소 공간 내에 직접적으로 영역을 예약 • 예약된 영역에 물리적 저장소를 커밋 • 필요한 보호 특성을 설정
  • 4.
    1. 주소 공간내에 영역 예약하기 LPVOID 예약된 메모리 주소 VirtualAlloc( 가상 메모리를 예약하는 함수 LPVOID lpAddress, 예약하고자 하는 메모리 주소 SIZE_T dwSize, 영역의 크기 DWORD flAllocationType, 예약할 것인지 커밋할 것인지 DWORD flProtect 보호 특성 ); 가상 메모리를 예약하는 함수
  • 5.
    LPVOID 대부분의 경우 NULL값을 전달. VirtualAlloc( 시스템이 프리 주소 영역 중 가장 적절 한 공간을 찾아낸다. LPVOID lpAddress, SIZE_T dwSize, 주소를 지정할 때에는 DWORD flAllocationType, 꼭 프로세스의 유저 모드 파티션을 가리 키는 값을 전달해야 한다. DWORD flProtect ); 주소는 할당 단위어야 한다. (13장 sec3 참조)
  • 6.
    LPVOID 시스템은 항 상 CPU의 페이지 크기의 VirtualAlloc( 배수로 영역을 예약 LPVOID lpAddress, 4KB, 8KB, 16KB 페이지 크기를 사용하 SIZE_T dwSize, 는 머신에서 DWORD flAllocationType, 62KB를 예약하려 하면 64KB로 예약한다. DWORD flProtect );
  • 7.
    LPVOID MEM_RESERVE: VirtualAlloc( • 영역 예약 MEM_TOP_DOWN: LPVOID lpAddress, • 단편화를 피하기 위해 높은 주소 공 SIZE_T dwSize, 간 상에 영역을 예약하기 원할 때. DWORD flAllocationType, 다른 값들은 나중에 살펴봅니다. DWORD flProtect );
  • 8.
    LPVOID VirtualAlloc( 13장 section6 보호특성을 참조. P501 LPVOID lpAddress, PAGE_NOACCESS SIZE_T dwSize, PAGE_READONLY DWORD flAllocationType, PAGE_READWRITE … DWORD flProtect );
  • 9.
    LPVOID NUMA(Non-Uniform Memory Access) 머신에서 VirtualAllocExNuma( 수행되는 경우 성능 개선을 위해 특정 HANDLE hProcess, 노드에 탑재된 램으로부터 가상 메모리 를 확보할 때 사용한다. LPVOID lpAddress, SIZE_T dwSize, 참조 DWORD flAllocationType, 14.3 NUMA 머신에서의 메모리 관리 DWORD flProtect, DWORD dwPreferredNumaNode );
  • 10.
    2. 예약 영역에저장소 커밋하기 메모리에 접근하기 위해 커밋 커밋이 필요함. 할당된 물리적 저장소에 메모리 영역을 매핑하는 커밋은 페이지 크기 단위 것. 로 수행된다. 13장 section4 물리적 저 장소를 영역으로 커밋하 기. p496
  • 11.
    커밋 방법 LPVOID MEM_RESERVE 대신 VirtualAlloc( MEM_COMMIT을 사용한다. LPVOID lpAddress, dwSize 전달하여 전체를 커밋하지 않을 SIZE_T dwSize, 수 있다. DWORD flAllocationType, DWORD flProtect );
  • 12.
    3. 영역에 대한예약과 저장소 커밋을 동시에 수행하는 방법 LPVOID MEM_RESERVE와 VirtualAlloc( MEM_COMMIT을 동시에 사용한다. LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
  • 13.
    큰 페이지 단위(Laarge-PageGranularity)를 사용하게 되면 성능을 개 선할 수 있다. ‘큰 페이지’의 최소 크기를 얻어오는 함수 GetLargePageMinimum MEM_LARGE_PAGE를 사용하면 메모리 공간을 페 VirtualAlloc을 호출할 때 이징 불가능 영역으로 설정 하여 항상 렘에 유지된다. MEM_LARGE_PAGE 플래그를 사용. 성능↑
  • 14.
    4. 언제 물리적저장소를 커밋하는가 가상 메모리 기법의 유일한 문 제점은 언제 물리적 저장소를 커밋할지 판단해야 한다는 것.
  • 15.
    커밋되었는지 확인하는 방법 1.항상 커밋해 본다. – 가장 쉽지만 느려진다. 2. VirtualQuery로 확인한다. – 1번보다 느리다. 3. 페이지 커밋 여부를 따로 기록한다. – 복잡한 작업이 될 수 있다.
  • 16.
    4. 구조적 예외처리(SEH)를 사용한다. • 코드가 적고 • 가장 빠르다. SEH는 23, 24, 25장에서 자세히 다뤄진다.
  • 17.
    5. 물리적 저장소의 디커밋과 영역 해제하기 BOOL 해제할 때 MEM_RELEASE를 사용. VirtualFree( 해제 시에는 LPVOID lpAddress, lpAddress는 시작 주소 dwSize는 0이어야 한다. SIZE_T dwSize, DWORD dwFreeType 예약했던 영역을 한번에 해제해야 함. );
  • 18.
    BOOL 디커밋할 때 MEM_DECOMMIT을 사용. VirtualFree( 페이지 단위로 가능 LPVOID lpAddress, lpAddress 시작주소, dwSize 0이면 SIZE_T dwSize, 전체를 디커밋. DWORD dwFreeType lpAddress + dwSize가 페이지 중간쯤이면 ); lpAdress ~ lpAdress + dwSize까지 모든 페이지 디커밋
  • 19.
    6. 보호 특성변경하기 VirtualProtect로 변경 가능. 잘 활용하면 잠재적인 버그로부터 보호할 수 있다. 예) 메모리를 사용할 때만 PAGE_READWRITE 사용하지 않을 때 PAGE_NOACCESS로 변경해서 접근 위반 에러를 발생하게 하는 것.
  • 20.
    7. 물리적 저장소의내용 리셋하기 물리적 저장소 리셋: N개의 페이지 내용이 수정되지 않았다고 시 스템에게 알려주는 것. 수행 성능을 향상 시킨다.
  • 21.
    예) 페이지를 로드할때 물리적 저장소가 꽉 차있고 4번 페이지를 사용할 경우. 1 2 3 1 4 3 1. 2를 저장 2. 4를 로드 1 2 3 4 5
  • 22.
    예) 리셋을 사용하면 시스템은2번이 수정되지 않았다고 알기 때문에 2를 저장 안함. 1 2 3 1 4 3 1. 바로 4를 로드 성능 향상! 1 2 3 4 5
  • 23.
    주의! 할당할 때 리셋할 때 시작주소 내림 올림 할당크기 올림 내림 위와 같이 다른 이유는 실수로 페이지를 리셋해 버리는 것을 막기 위함. 또한 MEM_RESET은 항상 단독으로 사용되어야 한다. MemReset예제 실행하지 말자. 컴퓨터 왕 느려짐.
  • 24.
    주소 윈도우 확장 •AWE (Address Windowing Extension) • 32비트 윈도우에서 더 많은 메모리를 사용 하기 위한 방법.
  • 25.
    AWE 특징 • 디스크로 스왑되지 않는다. • 주소 공간보다 더 큰 램에 접근할 수 있다. • 프로세스 주소 공간에 보여지지 않는다. • 주소 윈도우 (Address Window)를 통해 접 근한다. – 코드양이 많아진다.
  • 26.
    주의 • SQL Server2012부터 AWE를 지원 안함. • http://msdn.microsoft.com/ko- kr/library/ms143179(v=sql.110).aspx
  • 27.
    16장. 스레드 스택 •시스템에서 직접 주소 공간을 예약한다. • 스레드 생성시 스택 메모리 공간 – 기본적으로 1MB의 주소 공간을 예약 – 두 개의 페이지만 커밋 • 스택 영역은 모두 PAGE_READWRITE 특 성
  • 28.
    스레드 스택의 페이지상태 메모리 주소 페이지 상태 0x080FF000 커밋된 페이지 (최상위) 0x080FE00 커밋된 페이지 (가드 특성) 0 0x080FD00 예약된 페이지 0 … 0x08000000 예약된 페이지 (최하위) 가드 페이지에 접근하면 현재 가드페이지를 해제 다음 페이지를 가드페이지로 지정.
  • 29.
    모두 사용하였을 경우 메모리주소 페이지 상태 0x080FF000 커밋된 페이지 (최상위) 0x080FE00 커밋된 페이지 0 … 0x08001000 커밋된 페이지 0x08000000 예약된 페이지 (최하위) 최하위 영역은 항상 예약 상태로만 두고 커밋하지 않는다. 0x08001000에 물리적 저장소를 커밋할 때 EXCEPTION_STACK_OVERFLOW 예외를 발생시킨다.
  • 30.
    최하위 페이지를 예약상태로만 유지하는 이유 메모리 주소 페이지 상태 0x080FF000 커밋된 페이지 (최상위) 0x080FE00 커밋된 페이지 0 … 0x08001000 커밋된 페이지 0x08000000 예약된 페이지 (최하위) 0x07FFF00 다른 용도로 커밋된 페이지 0 0x08001000 영역을 다 사용하고 0x08000000을 사용하면 접근 위반이 발생. 최하위 영역을 커밋한다면 0x07FFF000에 접근할 수 있다.
  • 31.
    스택 크기 변경방법 • IDE에서 변경 – VC++ 컴파일러 /F 옵션 – 링커 /STACK 옵션 • 코드에서 변경 – CreateThread, _beginthreadex 호출 시 지정
  • 32.
    1. C/C++ 런타임 라이브러리의 스택 확인 함수 스택을 주소 공간에 할당하더라도 사용 전까지는 물리적 저장소에 커밋하지 않는다.
  • 33.
    스택 확인 함수 오른쪽함수는 void SomFunction(){ 4KB 페이지 크기 시스템에서 int nValue[4000]; // 16,000바이트 4개의 페이지가 필요함 nValue[0] = 0; // 할당 작업 수행 } 스택은 2개만 커밋되어 있음 스택을 확인하는 함수가 필요.
  • 34.
    • 스택 확인함수는 사용되는 영역이 커밋되 었는지 확인. • 컴파일러는 스택 확인이 필요한 곳에 자동 적으로 스택 확인 함수 호출 코드를 포함.
  • 35.
    1. Summation 예제애플리케이션 StackOverFlow 발생 시 SHE를 사용하여 우 아하게 종료한다.
  • 36.