프로그래밍 관련 자료를 찾아보다가 정말 좋은 글을 보게 되었다. 메모리 구조 관련하여, 정말 자세히 그리고 직관적으로 알수 있게, 직접 프로그램을 돌려보고 나온 결과와 시각 자료를 적절하게 넣어서 메모리 구조에 관해서 비교적 쉽게 이해를 할 수 있었다.
바로 이 글인데
https://st-lab.tistory.com/198
이 글을 보고, 공부 한 것을 발췌, 요약을 할 것이다.
일단, 컴퓨터에 데어터 저장 공간은 크게 하드디스크, 메모리(ram), 캐시 메모리, 레지스터 이렇게 4개로 나뉠수 있다. 이 중 우리가 흔히 메모리라고 하는 ram에서의 구조에 관한 내요을 다룬다
메모리 구조
밑의 그림을 보면 address 위의 low 부터 밑의 high로 4가지 영역으로 나뉜다.
Text - 상수, 함수
Data - 정적 변수, 전역 변수
Heap - 동적 할당 변수
Stack - 지역 변수
위에서 언급한 변수/함수 들을 실행 했을 때 pointer를 확인 한 값은 다음의 그림과 같다.
상수, 함수의 주소 값이 가장 적고, 그 다음으로 비초기화, 초기화 변수(전역 변수), 정적 변수들은 값이 조금 더 크고, 동적 할당 변수가 그다음, 지역 변수1,2 가 그 다음이다.
변수에 대한 이름이 헷갈릴 수가 있다. 그렇다면, 다음 글을 참고 해보자
https://easywebs.tistory.com/29
상수 30의 값이 0x100000e64에 어떻게 들어가는가?
다음의 그림 처럼 30을 16진수로 바꾼 1e 00 00 00 으로 할당이 된것을 확인 할수가 있다. 30은 int 타입이고, int 타입은 4바이트의 크기를 갖는다. 그리하여 4칸을 갖고 거기서 30을 16진수로 바꾼 1e가 할당이 된것이다. 다른 3칸의 00 00 00과 함께
Overflow
할당된 메모리 영역을 넘어갈 때, overflow에러가 난다. 할당된 메모리보다 큰값을 넣으려고 할 때, 혹은 다른 메모리 영역을 침범하려고 할 때 overflow가 나오는 데, 우리가 흔히 마주하는 stack overflow와 heap overflow를 알아본다.
Stack Overflow
스택은 지역 변수, 함수가 돌아가는 동안 유지가 되는 메모리에 대한 영역이다. 함수가 끝이 난다면, 이 메모리는 해제가 되는데, 만약 해지가 되지 않고, 계속 돌아간다면 어떻게 될까. 대표적인 예시로 재귀함수로 어떻게 돌아가는지 확인해보면
func()에 대한 재귀가 무한하게 불리는 무한 loop가 형성 되었다. int a와 int b가 함수 한번이 불릴 때마다, 메모리가 할당 될 텐데, 어셈블리어를 확인해 보면 다음과 같다.
이 과정을 축약하면,
스택의 시작점을 스택에 넣고, 스택의 꼭대기 점을 스택의 시작점을 복사해 넣어주고, 16을 빼준다. 16은 함수가 16바이트를 쓴다는 의미이다. 시작점에서 -4를 뺀 값에서 1을 복사하고, 8을 뺀 값에서 2를 복사하여, 마지막 callq로 함수 0x100000f10으로 이동한다. 그런데 재귀 함수가 계속 돌아가고 있으므로 stack 메모리는 이전 함수 그대로 유지가 된다.
이런 과정을 반복하며, stack의 영역을 벗어 나게 되면, stack overflow가 생기게 된다.
Heap Overflow
heap overflow는 OutOfMemory 에러로 잘 알려져 있는 데, 힙 영역에 더이상 메모리를 끼울 수 없을 때 생긴다. Heap영역은 c, c++ / c#,java의 대표적인 차이점이라고 생각한다. c#과 java는 각각 CLR과 JVM에서 참조되고 있지 않은 메모리 영역을 해제 해주는 GC가 돌아가지만, c,c++은 그런 기능이 없어서 이를 따로 제대로 관리 해줘야 한다. 만약 그대로 놔둔다면 memory leak이 발생하고, 결국 OutOfMemory에러를 불러 일으킬수가 있다.
heap overflow가 생기는 과정은 다음 그림과 같이 확인 할수가 있다.
추상화 된 개념으로 만 알고 있던 메모리 구조와, stack overflow의 과정을 어셈블리어로 확인하니 더욱 직관적으로 이해 하게 된 것 같다. 기억을 되짚는 용도로 쓴 글이니, 제대로 된 정보는 최상단 링크에서 다시 꼼꼼하게 보는 게 좋겠다.