프로세스는 코드 영역, 데이터 영역, 스택 영역, 힙 영역으로 이루어져 있다.
코드 영역은 프로그램의 본체가 있는 곳이고, 데이터 영역은 프로그램이 사용하려고 정의한 변수와 데이터가 있는 곳이다.
포인터를 제외한 일반적인 변수는 선언할 때 그 크기가 결정된다. 즉 코드 영역과 데이터 영역은 프로세스가 실행되기 직전에 위치와 크기가 결정되고 실행되는 동안 변하지 않으므로 정적 할당 영역이라고 부른다.
스택 영역과 힙 영역은 다음과 같은 특징을 가지고 있다.
스택은 가장 먼저 들어간 데이터가 가장 나중에 나오는 자료 구조LIFO로, 스택에 데이터를 삽입하는 것을 push, 스택에서 데이터를 꺼내는 것을 pop이라고 한다.
int main() {
int a=1, b=2;
printf("main %d %d\n", a b);
add(a, b);
exit();
}
add(int c, int d) {
mul(c, d);
printf("add %d\n”/ c+d);
}
mul(int e, int f) {
printf("mul %d\n", e*f);
}
스택은 함수 호출 시 두 가지 작업을 구현하기 위해 사용한다.
첫 번째로 호출한 함수가 종료되면 함수를 호출하기 전 코드로 되돌아와야 하는데, 이 때 되돌아올 메모리의 주소가 스택에 저장된다.
두 번째로 스택은 변수 사용 범위에 영향을 미치는 영역scope을 구현할 때 사용된다.
함수에서 사용되는 지역 변수는 함수가 종료되면 사용한 공간을 반환해야 하는데, 지역 변수를 저장할 때 스택이 사용된다.
위의 예제 코드를 실행시켜 보면 main()
함수가 add()
함수를 스택에 되돌아올 메모리 주소1
과 add()
함수에서 사용될 지역 변수 c, d
의 메모리 주소를 넣는다.
add()
함수가 mul()
함수를 호출하면 스택에 메모리 주소2
와 mul()
함수에서 사용될 지역 변수 e, f
의 메모리 주소를 넣는다.
힙은 동적으로 할당되는 변수 영역이다.
대부분의 데이터는 데이터 영역에 할당되고 그 크기가 정해진다. 그러나 일부 데이터는 프로그램이 실행되는 동안 할당되는데 대표적인 경우가 malloc()
함수이다.
int main() {
int sarr[50];
int *darr;
darr=(int*)malloc(sizeof(int)*50);
free(darr);
}
sarr은 50개짜리 정수 배열이고, darr은 포인터로서 그 크기가 아직 정해지지 않은 상태이다.
sarr은 데이터 영역에 50개짜리 정수 배열이 메모리를 차지하지만, darr은 빈 상태로 실행되다가 darr=(int*)malloc(sizeof(int)*50)
을 만나면 그때 메모리를 할당받는다. 따라서 sarr은 데이터 영역에, darr은 힙 영역에 50개의 정수 배열을 만든다.
메모리 관리 측면에서 sarr은 사용하든 안 하든 프로세스가 종료될 때까지 메모리를 차지한다. 그러나 darr은 필요할 때 malloc()
함수로 메모리를 할당받았다가 필요 없어지면 free()
함수로 메모리 영역을 반환한다.
포인터, malloc()
함수, calloc()
함수 등은 메모리를 효율적으로 사용하기 위해 만들진 것이다. 어쩌다 한 번 쓰는 큰 배열을 처음부터 선언하고 끝까지 놔두는 일이 없어야 한다.
그림과 같이 프로세스 제어 블록이 모여 있는 큐가 있다. 새로운 프로세스가 생성
되면 malloc()
함수를 사용하여 프로세스 제어 블록의 구조체를 만들고 해당 큐에 삽입하며,
프로세스가 종료되면 free()
함수를 사용하여 해당 프로세스 제어 블록이 차지하던 메모리 공
간을 반환한다.
포인터와 malloc()
함수를 사용하면 필요할 때 메모리를 사용하다가 필요 없을 때 반환함으로써 메모리 관리를 효율적으로 할 수 있다.