0722TIL

황준승·2021년 7월 22일
0

프로세스 메모리 구조

UNIX 시스템은 실행 중인 프로세스에게 4GB의 가상 메모리 공간을 할당하는데, 상위 1GB는 커널이, 하위 3GB는 사용자 프로그램이 차지한다.

stack 영역

프로그램이 자동으로 사용하는 메모리 영역으로 함수 호출과 관계되는 지역변수와 매개변수가 저장된다. 함수 호출 시 생성되며, 함수가 끝나면 반환된다. stack 사이즈는 각 프로세스마다 할당되지만 프로세스가 메모리에 로드될 때 stack 사이즈가 고정되어 있어 런타임 시 stack 사이즈를 바꿀 수 없다. 명령 실행 시 자동으로 증가 또는 감소하기 때문에 보통 메모리의 마지막 번지를 지정한다.

heap 영역

필요에 의해 메모리를 동적으로 할당할 때 사용하는 메모리 영역으로 동적 메모리 영역이라고 부른다. C 에서 malloc() calloc() 등의 함수를 사용하여 메모리 크기를 할당할 수 있으며, 메모리 주소 값에 의해서만 참조되고 사용되는 영역이다.

위의 stack과 heap영역은 사실 같은 공간을 공유한다. heap이 메모리의 낮은 주소부터 할당되면 stack은 높은 주소부터 할당되는 식이다. 그래서 각 영역이 상대 공간을 침범하는 일이 발생할 수 있는데 이를 각각 stack overflow, heap overflow 라고 한다.

Data 영역 (BSS, GVAR)

프로그램이 실행될 때 생성되고 프로그램이 종료되면 시스템에 반환되며, 전역변수, 정적변수, 배열, 구조체 등이 저장된다. Data 영역은 다시 BSS 영역과 Data(GVAR) 영역으로 나누어지는데, 초기화된 데이터는 Data 영역에 저장되고, 초기화되지 않은 데이터는 BSS 영역에 저장된다.

Text (Code) 영역

텍스트 영역은 실행 명령을 포함하는 코드들이 들어가는 부분이다.
프로그램을 시작 할 때 컴파일한 프로그램(기계어)이 저장되어 있고, 읽기 전용 영역이기에 프로세스가 함부로 변경 할 수 없고 변경 시 오류를 발생시킨다.

메모리 관리 프로그램을 만들어 보자.

  1. stack과 heap영역의 메모리를 관리를 쉽게하기 위해 map자료형을 통해 구현
    (heap은 아래에서 위로, stack은 위에서 아래로 저장되므로 아래의 그림과 같이 형성하였다.)
    (그림에서 보는 것처럼 메모리 주소는 1씩 증감하는 걸로 나타내었습니다.)
    // 초기 함수 구현
    init(stackSize,heapSize){
        maxHeapSize = heapSize;
        maxStackSize = stackSize;

        for (var i = 0; i < (stackIdx / 2); i++){
            heap.set(i ,{'size' : 0,'type': "", 'address': 21});  
        }

        for (var i = 20; i >= (stackIdx / 2); i--){
            stack.set(i ,{
                'name' : "",
                'size': 0,
                'address': heapIdx
            });
        }
    }
  1. call함수
  • 함수가 stack에 쌓이는 과정
    예시 코드)
#include<iostream>
void foo(){
	int a;
    int b;
};

int main(){
	int a;
    double b;
    
    foo();
    bar();
    
    return 0;
}

1) 메인 함수가 실행되어 int a; double b;가 실행되는 모습을 스택으로 표현

2) foo()함수가 실행되는 모습(리턴까지)을 스택으로 표현

만약 이때 리턴이 실행된다면 주황색으로 감싸진 모든 스택이 없어집니다.

  • call함수
    call(name, paramCount){
        stackIdx -= (paramCount + 2);
        nowStackSize += 8 + 4 * (paramCount + 1);
        if (nowStackSize <= maxStackSize){
            stack.set(stackIdx,{'name' : name, 'size': nowStackSize, 'address': -1});
        } else{
            console.log("Heap is Full");
        }
        
    }
  • returnf함수
    returnf(name){
        let re = [];
        for(var i = 20; i >= stackIdx; i--){
            let st = stack.get(i)['name'];
            if (st != ''){
                if (st === name){
                    break
                }
                re.push(i)
            }
        }

        stack.set(i,{'name': '', 'size': 0, address: -1})
        return re[re.length-1];

    }
  1. malloc 함수 (동적 할당)
  • 스택에서 호출된 함수의 reference 값을 힙 영역 저장, 그리고 malloc의 인자 값을 통해 힙영역의 얼마만큼을 할당하는 기능을 하는 함수입니다.
    malloc(type, count){
        let typeSize = sz.get(type)
        typeSize = Math.ceil(typeSize / 8) * 8 * count
        nowHeapSize += typeSize;
        if (nowHeapSize <= maxHeapSize){
            heapIdx += 1;
            heap.set(heapIdx,{'size': nowHeapSize, "type":type,'address': stackIdx});
            stack.get(stackIdx)["address"] = heapIdx
        } else{
            console.log("Heap is Full");
        }
    }
  1. free 함수 (GC)

call - return이 있듯이, malloc은 free 함수를 통해 종료합니다. - 해당 영역에 값을 reset

   free(pointer){

        const addr = stack.get(pointer)['address'];
        nowHeapSize -= heap.get(addr)['size'];

        if ( addr > -1) {
            heap.set(addr,{'size': 0, type: "",'address': 21});
        }
        else{
            console.log("malloc함수를 실행한 적이 없습니다.!!");
        }
    }
  1. 가비지 컬렉션
  • 메모리 관리 기법 중 하나로 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. 즉, 동적 할당된 영역 가운데 어떤 변수도 가리키지 않는 메모리 영역을 탐지 하여 자동으로 해제 하는 기법입니다.

    대표적으로 c에서는 malloc()으로 동적할당하였지만 스택에서 리턴(return)하여 그 값이 더 이상 쓸모없어졌을 경우(free를 하지 않았기 때문에 힙영역에 값이 살아있지만 그 값을 스택에서 참조 할 수 없음) gc를 통해 그 값을 다시 리셋시킬 수 있다.

profile
다른 사람들이 이해하기 쉽게 기록하고 공유하자!!

0개의 댓글