들어가며
'동적 할당'이라고 들어본 적 있는가?
프로그래밍을 처음 배우는 입장에서의 이 단어는 매우 생소하기 그지없다.
그렇다고 필자라고 해서 익숙한가? 전혀 그렇지 않다.
급하게 뒤로 가기 버튼이 마려워지는가?
같이 배워가는 입장에서 너무 그러지 말길 바란다.
그만큼 이 개념을 처음 접하는 사람의 입장과 시각에 입각해
바라볼 수 있기에 더 쉽게 설명할 수 있지 않겠는가?
그렇기에 신뢰해주길 바란다.
아 믿어달라고.
메모리는 어떻게 생겨먹었는가
메모리(Memory).
듣기만 많이 들어봤지, 구조나 구성이나 우린 그런 거 아무것도 모른다.
알아도 일단 아무튼 모른다고 가정하자.
프로그램이 실행되기에 앞서, 프로그램은 메모리에서 로드(Load, 불러오기)되어야 한다.
또한, 프로그램에서 사용되는 변수들도 어딘가에 저장되어야 하므로 메모리가 필요하다.
따라서 컴퓨터의 운영체제는 프로그램의 실행을 위해 다양한 메모리 공간을 제공한다.
다음의 그림은 운영체제가 제공하는 메모리 공간을 표현한다.
보이는 바와 같이 메모리 공간은 4가지로 구분된다.
1. 코드 영역 (Code Segment)
메모리의 코드 영역은 실행할 프로그램의 코드가 저장되는 영역으로,
텍스트 영역이라고도 한다.
CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 된다.
2. 데이터 영역 (Data Segment)
메모리의 데이터 영역은 프로그램의 전역 변수(Global)와 정적 변수(Static)가 저장되는 영역이다.
데이터 영역은 프로그램의 시작과 함께 할당되며,
프로그램이 종료되면 소멸한다.
3. 스택 영역 (Stack Segment)
메모리의 스택 영역은 함수의 호출과 관계되는
지역 변수(Local)와 매개 변수(Parameter)가 저장되는 영역이다.
스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다.
이렇게 스택 영역에 저장되는 함수의 호출 정보를
스택 프레임(Stack frame)이라고 한다.
스택 영역에 데이터를 입력(저장)하는 것을 '푸시(Push)'라고 하고,
데이터를 출력(인출)하는 것을 '팝(Pop)'이라고 한다.
이러한 스택은 '후입 선출(LIFO, Last-In First-Out)' 방식에 따라 동작한다.
따라서 가장 늦게 저장된 데이터가 가장 먼저 출력된다. (쉬운 예를 들자면 과자 '프링글스'를 떠올려보라.)
또한, 메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다.
4. 힙 영역 (Heap Segment)
메모리의 힙 영역은 사용자가 직접 관리할 수 있는, 그리고 '해야만 하는' 메모리 영역이다.
힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제될 수 있다.
또한, 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
'스택 프레임'에 관한 간단한 고찰
'스택 프레임'에 관하여 말하기 전에,
'스택'을 정말 간단 명료하게 설명하자면 과자 '프링글스'라고 생각하라.
후덕한 인상의 인심좋아 보이는 수염아저씨가 표면에 그려져있다.
사람은 참 좋아보이지만 사실 우리들의 뇌혈관 질환과 심장 질환을 위해서
나트륨을 거하게 빨아제낀 흉악한 캐릭터이다.
아무튼 가장 나중에 들어간 칩(과자)은, 우리가 가장 먼저 먹는다.
그리고 이후에는 하나 하나 차례대로 꺼내먹지 않는가?
이것이 '스택'의 '후입 선출' 구조이며 전부라고 봐도 무방하다.
스택의 개념은 이제 알겠고, 그래서 '스택 프레임(Stack Frame)'이란 무엇인가.
뭔가 그럴싸해 보이고 뭔가 어려울 것 같은 이름이지 않은가?
실상은 아주 단순한 녀석이다.
함수가 호출되면 스택에는 함수의 매개 변수,
호출이 끝난 후 돌아갈 반환 주솟값, 해당 함수의 지역변수 등등이
'스택 영역'에 저장된다.
그 스택 영역에서 차례대로 저장되는 함수의 호출 정보를
'스택 프레임'이라고 한다.
이를 이해하기 위해 다음의 코드를 사용해 보도록 하겠다.
int main()
{
func1();
return 0;
}
void func1()
{
fun2();
}
void fun2()
{
}
다른 거 없이 단순하게 함수 호출만을 반복하는 코드이다.
실행시켜 봤자 눈으로 봐서는 아무것도 달라지지 않으니
일단 흐름 정도만 읽어도 충분하다.
그 흐름은 다음과 같다.
[그림 2]에서 보이듯이 스택 프레임이라는 것은
데이터를 차례대로 저장하고 있는 하나의 큰 틀을 지칭하는 것이다.
그리고 스택은 이러한 구조나 과정을 가진 매커니즘을 지칭하는 것이다.
이러한 흐름이 메모리 공간 중에서도 '스택' 영역에서 발생한다.
우리는 간단하게 코드만 뚝딱뚝딱 치고 결과를 볼 수 있지만,
메모리에서는 시나브로 이런 논리적인 과정들이 발생하고 있다.
'스택 오버플로우'에 관한 간단한 고찰
메모리는 물리적인 공간이다.
이는 스택 영역을 비롯한 메모리 전체에 대해서
저장 한도, 즉 한계가 존재한다는 뜻이기도 하다.
만약 재귀 함수를 무한으로 즐기게 된다면 어떻게 될까.
굳이 보지 않더라도 너무 무서운 결과가 기다리고 있을 것만 같다.
그래도 구태여 우리는 봐야 할 필요성이 있다.
우리는 지금, 배우는 입장이지 않은가?
그럼 다음의 그림을 보도록 하자.
점점 메모리 상의 공간을 차지하고 여유 공간이 줄어들며
마침내 더 이상 저장 공간이 사라지면,
해당 데이터는 스택 영역을 넘어가서
다른 영역을 침범한 상태로 저장되게 된다.
그렇게 된다면······.
이 현상의 이름처럼 넘쳐흐르게(Overflow) 되어버린다.
해당 프로그램은 의도치 않은 동작을 수행하게 되거나
보안상의 크나큰 취약점을 가지게 된다.
이때를 대비해서 C언어에서는 대비책을 하나 강구해냈다.
스택 오버플로우가 발생하게 되면, 컴파일러는 너굴맨 마냥
에러를 발생시키고 곧바로 실행을 강제로 종료시킨다.
깔끔하게 처리했으니 안심해도 된다는 저 믿음직스러운 표정을 보라.
참으로 든든하지 않은가?
마치며
이로서 우리가 키보드 뚝딱이며 코드를 치고 실행했을 때
메모리 공간에서 일어나는 일 중에서도
가장 알아야 하는 스택 영역에 대해서 알아 보았다.
뿐만 아니라 구조와 구성도 알아보았다.
그럼 다음 시간엔?
다음 시간에 계속······.
참고 및 출처
코딩의 시작, TCP School
코딩 도장