인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

jeri님의 프로필 이미지
jeri

작성한 질문수

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문

스택 확인 함수 관련 질문입니다

작성

·

218

0

C++ 강의 너무 좋다고 수강 후기 썼던게 어느덧 한달 전인데 그 사이에 게임 회사 붙어서 프로그래머로 회사 다니고 있습니다
정말 이렇게 게임회사를 다니게 될거라고 생각을 못했어서 질문을 하기전에 다시 감사인사드립니다! 회사다니면서도 제가 모자란 부분이 많아서 강의 계속 복습도 하고 추가 공부도 하고 있어요
오늘도 헷갈렸던 부분을 강의도 듣고 구글링하고 있었는데 질문이 생겨서 글 올립니다!
질문은
void f()
{
   int arr[3000];
   arr[0] = 0; 
}

이런식으로 배열을 할당하고, 배열에 어떤 값을 할당을 하는 함수가 있다고 했을 때,

위와 같은 할당 방식은

스택에서 스택 공간을 할당은 바로 하지만, 실제 접근이 이루어지기 전까지는 물리적 저장소를 커밋하지 않으니까

위에서처럼 할당을 할 때 만약 가드 페이지보다 아래에 접근을 하게되면 접근 예외가 이루어지게 될텐데요

관련 설명을 더 찾아보다보니까

컴파일러들은 컴파일 과정에서 함수를 만나게 되면 각 함수들이 필요로 하는 스택의 크기를 결정하게 된다. 만일 함수가 필요로 하는 스택의 크기가 개별 CPU의 페이지 크기보다 더 큰 메모리를 필요로 하는 경우, 컴파일러는 자동적으로 스택 확인 함수를 호출하는 코드를 삽입한다.

라는 설명이 있었는데, 함수가 필요로 하는 스택의 크기가 개별 CPU의 페이지 크기보다 더 큰 메모리를 필요로 하는 경우라는 부분이 이해가 잘 안됩니다

코드에서 할당 작업을 수행(물리적 저장소에 커밋이 되어있어야함)을 하려고 할 때,

위의 함수에서 할당 작업을 하려면 스택 사이즈가 적어도 12,000byte여야 할텐데

그러면 x86/64의 경우 시스템의 페이지 크기인 4kb를 초과하게 되고

페이지보다 더 큰 메모리를 필요로 하게되는데,

스택 확인 함수를 호출한다는건 페이지 세개를 쓸때까지..?

물리적 저장소에 커밋하고 가드 페이지를 옮기는 작업을 해주는 역할을 한다는게 맞을까요?

위에서 예약만 수행된 메모리에 대한 접근을 할 수도 있다고 했으니까요..

그니까 결론이 그냥 접근만 하면 이런 커밋-가드페이지 옮기는 작업이 되지 않으니까

스택 확인 함수를 호출해줘서 접근 예외를 일으키지 않게 하자!

라는 느낌인게 맞는게 궁금합니다.

그리고 또 스택 부분을 공부하다보니까

그림으로 그려보면 이런 느낌인데

예약된 스택으로 예약된 페이지를 스택의 최하위를 빼고 다 쓰고나면 스택 오버플로우가 발생하는 건지, 아니면 위의 그림에서 우측 상태에서 뭐 하나를 더 추가하려고 하면 스택 오버플로우가 발생하는건지 헷갈립니다.!

그리고 또 거기도 넘어서 최하위에 뭔갈 쓰게 되면 스택 언더플로우라는걸까요? 아니면 스택의 최하위를 넘어서는 곳(그림에 점선 박스로 표시한 곳)에 뭐가 쓰이면 언더플로우인걸까요?

질문이 너무 많은 것 같네요 ㅠㅠ.. 강의 정말 잘 듣고 있고있습니다!

답변 1

1

Rookiss님의 프로필 이미지
Rookiss
지식공유자

1.
우선 스택 메모리의 메모리 커밋 관련 부분까지 들어가는건
너무 지나치게 세세하게 들어가는 것 같다는 생각이 드네요.
이 부분은 OS마다 다를 수도 있고, 컴파일러 설정에 따라서도 또 달라지기 때문입니다.

기본적으로 어떤 함수가 호출되면 함수 내부적으로 사용하는 로컬 변수들을 보고
컴파일러가 사용할 스택 영역을 잡아주도록 
esp (혹은 64비트라면 rsp) 레지스터를 쫙 내려주게 됩니다.
(함수 호출 규약에 따라 이 부분에 코드 어디에 들어가는지는 조금씩 다를 수 있습니다)

따라서 사람들이 은근 착각하는게

int arr[3000];

이것과

int arr[1];

이것과 비교하면 전자가 더 메모리를 '많이' 할당하니 뭔가?
더 메모리를 많이 잡아 먹고 오래 걸릴 것 같지만
만들어진 코드를 보면, 어차피 이미 할당된 스택 메모리에
esp만 조절해서 사용하는 것이므로 완전 똑같다는 것이죠.
물론 말씀주신 상황처럼 극악으로 큰 메모리를 할당하면
추가 작업이 필요할 수도 있긴 하지만
사실 이렇게 스택에 큰 데이터를 활용할 일은 사실상 없으므로
그 정도까지 극단적으로 생각할 필요는 없습니다.

2.
그 다음 스택 오버플로우에 관한 질문을 주셨는데,
크게 구분하면 2 가지 케이스로 구분할 수 있습니다.

진짜 스택 메모리가 고갈되는 경우
재귀 함수를 잘못 호출했거나, 함수를 순환 관계 (A->B->A->B)로 호출해서
영영 끝나지 않고 함수가 서로를 무한으로 호출하다가,
스택 프레임을 구성하는 부분으로 인해
나중에 가서 정말 스택 메모리 부족으로 터지는 경우입니다.
스택이 높은 주소->낮은 주소로 증가하니 뭐 언더플로우라고 말해도 틀린 것은 아니지만
그냥 개념적으로 '스택 오버플로우'라고 더 많이 표현합니다.
스택 콜을 봤더니만 동일한 이름의 함수가 200개 뜬다? 이러면 이런 상황 100%입니다.

버퍼를 잘못 접근해서 스택 메모리를 터뜨린 경우

두번째로는 스택 메모리가 고갈된 것은 아니지만, 할당 받은 영역을 넘어서 사용한 경우인데요.
int arr[3000];

위 예제에서 이렇게 세팅을 해 놓고,
for (int i = 0; i < 4000; i++)
     arr[i] = 3;

이렇게 버퍼 한도를 초과해서 사용하면,
원래 유효하게 사용 가능하도록 찝어준 스택 메모리 영역을 침범해서 
타인의 스택 프레임 영역을 엉뚱한 값으로 덮어쓰겠죠.
이런 상황도 스택 오버퍼플로우라고 합니다.
보통 이럴 경우 상황이 난해해지는데 함수가 끝날 때
stack buffer corrupted~~ 이라면 궁시렁 거리며 디버그 모드에서 잡아주는 경우도 있고
그냥 빠져 나오다가 EBP EIP가 오염되어 크래시 나는 경우도 있고 아주 다양하게 행동합니다.

뭐 이렇듯 여러가지 용어가 혼용되고
그냥 스택 메모리가 터진 상황을 '스택 오버플로우'라고 한다고 포괄적으로 생각하시면 됩니다.

PS:
그리고 취업하셨다니 축하 드립니다 ㅎㅎㅎ
취업 소식을 전해주신 6번째 분이 되셨군요! (사실 제가 딱히 한 건 없지만 괜히 뿌듯한..)
취업했다고 안심해서 바로 손을 놓지 않고, 앞으로도 꾸준히 공부해서 원하시는 갓겜을 다 만드시기 바랍니다.

jeri님의 프로필 이미지
jeri
질문자

감사합니다 헷갈렸던 부분이 이해됐습니다ㅎㅎ

정말 긴 터널 속의 빛이었습니다 ㅠㅠ

앞으로도 열심히 공부하겠습니다 ㅎㅎㅎ!

jeri님의 프로필 이미지
jeri

작성한 질문수

질문하기