메모리 영역은 크게 5가지로 나뉜다. Windows Linux 차이는 있지만 기본적인 뼈대는 OS 모두 같을 것이다.

 

1. 메모리 영역

메모리 영역은 위와 같이 5개로 나뉜다. 기본적으로 code, data, bss 컴파일 과정에서 사이즈가 할당되며 해당 사이즈는 고정으로 변하지 않는다.

기본적으로 코드를 작성해서 컴파일 .exe 같이 실행파일을 만들게 되면 .exe 같은 실행파일에는 bss, data, code 데이터만 들어가 Hard Disk 저장된다.

 

그리고 해당 파일(.exe) 실행하면 bss 메모리에 로드하고 bss 통해서 지역 stack 주소 값을 계산한다.

이후에는 stack 저장된 int a = 4 같은 지시문이 실행되면서 4 byte공간을 할당하고 a라는 변수에 4라는 값을 저장 stack (지역) 실제적으로 메모리 할당한다. Heap 마찬가지로 malloc등의 지시자를 runtime 과정에서 실행하면서 Heap 영역에 메모리를 할당한다.

 

결과적으로 stack, heap 저장되는 지역변수, 동적변수는 실제 .exe파일을 실행하기 전에는 메모리 할당 data 저장된 공간이 존재하지 않는다. 해당 공간은 .exe 실행함으로써 메모리 할당이 이루어지고 실제적으로 데이터가 저장된 주소 공간이 만들어 지는 것이다.

 

<heap bss경계선을 기준으로 위로 올라가고 stack 최대 주소 ( FFFFFFFFF)에서 부터 아래로 점점 내려오는 구조로 메모리를 할당한다.(FFFFFFFF에서 4byte할당 FFFFFFFC 되는 형태이다)>

 

1) Code

Code 메모리의 가장 아래 공간에 위치하며 해당 공간을 text라고도 한다. 보통 기계어가 저장되는 공간이다.

또한 함수와 지시문, 분기문, 상수 등이 저장되는 공간이다. Code영역은 실제적으로 기계어로 지시되며 .exe파일 실행시 명령문들을 담고있다. 해당 영역은 컴파일 시에 결정되며 이후 Read-only성격으로 수정이 불가능하고 프로그램 종료시 까지 메모리에 올라와있게 된다.

 

2) Data(bss포함)

data영역은 전역변수와 정적변수(static) 저장되는 공간으로 프로그램 시작 할당되며 프로그램 종료시까지 유지된다.

data영역은 3가지로 나뉘게되는데

  • readonly
  • read&write
  • 미할당

위와 같이 3가지 영역으로 구분된다.

readonly 경우 .rdata 라고 하며 해당 영역에 저장되는 값들은 const, final, 문자열과 같은 상수 값들이다. 그리고 write & read 경우에는 .data영역이라고하며 기본적으로 정적 변수 등이 저장되는 공간이다. 마지막으로 미할당 영역은 BSS영역이라고 하며 선언은 되었으나 아직 값이 할당되지 않은 변수들에 대한 부분으로 main문이 시작되기 전에 NULL 값을 가지는 변수들로 동적 할당 등을 통해 main문이 실행 되는 runtime 과정에서 추가적으로 값이 할당된다.

 

ex) char *ptr ="Hi"; 같이 전역변수를 선언하면

ptr이라는 변수는 .data영역에 저장되고 "Hi"라는 변수는 .rdata 저장된다.

 

3) Stack

지역 변수들이 저장되는 공간으로 push, pop 통해 메모리 할당과 해제가 진행되며 빠른속도를 바탕으로 메모리를 필요에따라 할당하고 해제하는 공간이다.

함수 호출 해당 함수의 메모리, 지역 변수 등을 할당할 있으며 잘못된 재귀 등을 통해 너무 많은 변수 함수를 할당하게되면 stack영역을 넘어서 heap영역까지 도달하게 되고 경우에 stack overflow 문제가 발생한다.

 

stack영역은 기본적으로 프로그램 실행을 위해 사용하는 함수나 변수들의 메모리를 할당하는 임시 공간과 같은 개념으로 변수와 함수의 역할이 끝나면 자동으로 메모리 공간을 해제하는 장점을 가지고 있다. 하지만 heap 경우에는 컴파일러나 가상머신이 해제를 도와주긴하나 원칙적으로 할당된 메모리 공간은 자동으로 해제되지 않고 남아있게된다. 때문에 안전한 프로그래밍을 위해서는 개발자에게 자신이 할당한 메모리 공간을 해제하기 위한 노력이 반드시 요구된다.

 

  • Stack 레지스터

기본적으로 stack 변수, 함수를 할당하고 해제할 때는 register 함께하며 register 대한 이해가 필수적이다. 가장 핵심이 되는 레지스터는 SP(Stack pointer), BP(Base pointer), IP(Instruction Pointer)이다. CPU bit 따라 ESP, EBP, EIP 불리기도 한다.

 

기본적으로 SP 스택포인터 스택의 현재 진행 중인 지점을 가리킨다. 그리고 BP 경우 스택의 경계 BP stack 맨위(heap 가장 ) 곳인 stack 시작점을 가리키며 함수 구분을 위해 함수의 가장 base가되는 주소값을 가리킨다. 예를들어 main문이 시작되면 맨처음 main문이 시작된 스택 주소 값을 BP 가리킨다. 그리고 변수들이 할당되면 SP 같이 stack에서 heap쪽으로 값이 push되며 heap쪽으로 내려 것이다. 여기서 만약 다른 함수를 호출하게된다면 해당 함수의 내용이 SP 기준으로 push될텐데 함수의 시작 주소값으로 BP 옮겨준다. 그리고 해당 함수에서의 변수 할당 등이 진행되면 SP main때와 마찬가지로 내려가며 값들을 push 것이다. 해당 함수가 리턴되고 다시 main문으로 나온다면 BP 역시 main문의 시작 주소 값으로 바뀌게 된다. 마지막으로 IP 실행할 명령어를 저장하는 공간이다.

 

  • Stack 할당 과정

sfp 함수의 시작지점을 의미하고 rip 다음 실행할 명령을 저장해두는 공간이다.

 

위와 같이 int a=4, int b=5 순서대로 실행되면 stack에서는 esp 이동해가면서 실제적으로 a=4 b=5 push한다.

 

여기서 test라는 함수를 호출하면 stack 위와 같이 된다. main문에서 tset()함수를 호출함으로써 CPU test함수 주소 값으로 이동해서 작업을 진행한다. test()함수 리턴 main문을 이어서 진행해야 하므로 test 함수 리턴 이후 진행해야되는 주소 값에 대한 정보를 rip 저장하여 stack push한다.

그리고 새로운 함수 test() 대한 함수 시작지점인 sfp 만들고 ebp, esp 해당 sfp MOV 명령을 통해 이동시켜준다.

그리고 위와 같이 test() 함수 존재하는 명령을 수행하며 변수 c 대한 할당을 진행한다. 경우 main때와 마찬가지로 esp 이동하며 변수를 할당한다.

 

그리고 함수가 리턴되면 esp ebp 위치로 이동한다. 해당 함수의 시작 주소값이 있는 곳으로 MOV명령을 통해서 SP 레지스터를 옮겨준다.

 

그리고는 pop 해주면서 ebp 이전 함수의 시작지점으로 이동되고 esp 경우 rip 가리키게 된다. 그리고 rip 읽어와서 다음으로 실행할 주소 값을 얻어온다.

그리고 이와 같이 stack 정리된다. 변수 할당이 진행된다면 esp 기준으로 값이 push되면서 현재 rip으로 되어있는 값에 덮어쓰기가 것이다.

 

여기서 마지막으로 함수를 실행하는 과정을 조금 자세히 살펴보면 아래와 같다.

함수를 실행한다는 것은 해당 함수에 해당하는 명령어(지시어) PC, IR 레지스터에 올리고 해당 명령을 실행하는 과정을 의미한다. 기본적으로 함수 명령어는 Code영역에 저장되어 있다 함수 호출을 진행하게 되면 PC 레지스터에 Code영역에 존재하는 명령어의 주소 값을 넣어주고 IR 해당 값을 읽어와 실행하게 되는 것이다. 그리고 해당 명령문을 실행하는 과정에서 발생하는 변수 할당 함수 리턴 실행해야되는 다음 명령문 등에 대한 정보(rip, sfp)를 모두 stack 저장하는 것이다.

 

위와 같은 과정으로 프로그램이 동작하기 때문에 stack 영역을 프로그램 동작을 위해 사용하는 임시영역이라고 하는 것이다.

 

4) Heap

Heap 동적할당이다. vmmap으로 프로그램의 메모리 구조를 살펴보면 동적으로 값이 할당 되었을 [heap]으로 중간에 갑자기 메모리가 할당된 것을 확인할 있다. 주소 처음도 끝도 아닌 중간인 이유는 heap 할당 공간위치가 code, data stack 사이에 존재하기 때문이다. 가장 처음인 0x00000000공간에는 code, data 대한 값들이 메모리 할당된 것을 확인할 있고 0xFFFFFFFFF 마지막 부분에는 stack 대한 값들이 거꾸로 메모리를 할당하고 있음을 확인할 있다.

위와 같이 heap bss경계선에 해당하는 중간 부분에 위치하기 때문에 동적으로 할당된 값의 시작 주소 값을 보면 처음도 끝도 아닌 중간임을 확인할 있다.

 

이러한 heap 실제적으로 Chunk 형태로 할당되고 해제되는데 heap 대한 자세한 내용은 양이 많아 따로 정리가 필요할 같다..

 

 

 

 

+ Recent posts