반응형

 프로세스에서 스레드가 생성되면 운영체제는 프로세스의 주소 공간에 스레드 스택으로 사용할 영역을 예약하고 

 영역의 일부만 물리적 저장소에 커밋합니다. 

 일반적으로 스레드를 생성하면 1MB 주소 공간으로 설정됩니다. 

 ( 스택의 메모리 크기는 지정할 수 있습니다. 하지만 설명을 위해서 1MB라고 가정합니다.)

스택 메모리의 구조 

 스레스 생성시 스택 메모리 구조

 스택 메모리가 처음 생성되었을 당시의 스택 메모리 구조 입니다.

 스레드가 생성되면 1MB(256개의 페이지)인 스택 메모리가 프로세스의 주소공간에 예약을 진행합니다. 

 예약된 모든 공간이 Commit되지는 않고 그중에 최상위 주소공간의 2페이지만 Commit 됩니다. 

 실제로 Commit된 페이지중에 상위 페이지만 사용 가능하고 나머지 페이지는 추가적인 메모리 할당 여부를 

 확인하기 위해서 사용합니다. 

 스레드가 시작되기 전에 스레드의 스택 포인터 레지스터는 스택으로 예약된 영역의 최상위 페이지의 끝을 가리키도록

 설정됩니다.

스레드 스택 메모리의 가드 페이지 보호 특성

가드 페이지 보호 특성을 가진 페이지 접근시

 반복적인 스레드의 함수 호출로 스레드의 SP 레지스터가 가드 페이지 보호 특성을 가진 페이지를 접근하려고 하면 

 시스템이 그 사실을 인지하고 가드 페이지 다음에 추가적인 페이지를 Commit합니다. 

 이전에 가드 페이지에서 특성을 제거하고 새로 Commit한 페이지에 가드 페이지 특성을 설정합니다.

 이러한 기법을 사용해서 호출 트리가 깊어지면 추가적인 Commit작업을 진행합니다. 

스택 오버플로우

스택 오버플로어 상태

 스레드의 호출 트리가 깊어져서 최하위의 바로 이전 페이지를 커밋하게 되면 이전에 진행하던 형식과 다르게

 동작 합니다. 예제에서는 0x07001000 페이지가 커밋 될 때 이전과는 다르게 보호 특성을 지정하지 않습니다.

 이것은 스택으로 사용할 수 있는 모든 페이지가 커밋 되었다는 뜻입니다. 

 그리고 추가적으로 EXCEPTION_STACK_OVERFLOW의 예외를 발생시킵니다. 

 일반적으로는 여기까지 진행되었다면 더이상 스택을 사용하지 않지만 예외가 발생한 이후에 계속해서 스택을

 사용하게 되면 최종적으로 최하위 페이지까지 접근을 시도하게 되는데 이때에는 접근위반을 발생시키기 위해서 

 최하위 페이지는 절대로 커밋하지 않습니다. 

 만약에 최하위 페이지가 커밋되어 있다면 오류없이 스택메모리를 사용될 것이고 0x070000000 주소를 넘어서

 사용하려고 할 것이고 만약에 그곳에 메모리가 할당되어 있다면 데이터가 덮어쓰여지는 문제를 인지 할 수 없는

 문제가 발생합니다. 

소스 코드를 통한 스택 메모리 확인

아래의 재귀 함수를 스레드에서 호출해서 스레드 스택 메모리가 어떻게 변하는 지 확인 해 보았습니다.

// 함수 호출 1번당 1KB 스택메모리를 사용합니다.
void RecursiveReadFun(DWORD Count)
{
    BYTE PAGE[1024];
    memset(PAGE, Count, sizeof(PAGE));

    if (Count > 1)
    {
        RecursiveReadFun(Count - 1);
    }

    return;
}

// 스택 메모리를 확인하기 위한 재귀함수
DWORD WINAPI StackMemoryCheck(PVOID pArg)
{
    RecursiveReadFun(2000);
    return 1;
}

스레드 함수가 처음 수행되었을때 스택 메모리 구조

 StackMemoryCheck 스레드 함수가 처음 수행 되었을 때 스택 메모리 구조를 비주얼 스튜디오로 디버깅한 그림입니다. 

 예제에서 스택 메모리는 0x000000A6'6DD0000 ~ 0x000000A6'6CD0000 예약되었고

 최상위 페이지와 그다음페이지(총 2페이지)가 커밋되어 있는 상태인것을 확인 할 수 있습니다.

재귀함수가 13번 호출되었을때 스택 메모리 상태

 재귀함수가 13번 호출 되면서 추가적인 페이지들이 commit 된 것을 확인 할 수 있습니다.

 재귀함수가 계속 호출되면 페이지들이 계속해서 commit을 진행하게 됩니다. 

StackOverFlow 예외 발생

 예제를 통해서는 재귀함수가 1392번 호출 되었을 때 StackOverFlow가 발생하는 것을 확인 할 수 있습니다. 

 예외가 발생되었을 당시의 메모리 구조를 디버깅 해보았는데 최하위 페이지의 영역은 커밋이 되지 않은 것을

 확인 할 수 있습니다. 

 (해당 함수는 새롭게 수행해서 메모리 주소가 변경되었는데 스레드 스택 메모리의 범위가

 0x00000079'FD900000 ~ 0x00000079'FDA00000 가집니다.)

최하위 페이지
최상위 페이지

반응형

+ Recent posts