[OSTEP] 14. 메모리 API (Memory API)
안녕하세요, pingu52입니다.
이전 13장에서는 메모리 가상화의 개념인 주소 공간을 다루었습니다. 이번 14장은 실제 UNIX/C 프로그래밍 환경에서 메모리를 할당하고 관리하는 API와 주의사항을 다룹니다.
C 언어의 malloc과 free 함수는 메모리 관리의 핵심이지만, 올바르게 사용하지 않을 경우 치명적인 오류를 발생시킵니다. 이번 글에서는 이들의 정확한 사용법과 흔한 오류 사례, 그리고 운영체제 차원의 지원 기능을 정리합니다.
1. 메모리의 종류: 스택(Stack) vs 힙(Heap)
C 프로그램이 실행될 때 할당되는 메모리는 관리 방식에 따라 크게 두 가지로 나뉩니다.
1.1 스택 메모리 (Stack Memory)
- 관리 주체: 컴파일러 (암묵적 할당 및 해제)
- 특징: 함수 호출 시 지역 변수 등을 저장하기 위해 자동으로 할당되며, 함수가 반환되면 자동으로 해제됩니다. 이러한 특성 때문에 **자동 메모리(Automatic Memory)**라고도 합니다.
- 선언 예시:
void func() { int x; // 스택에 정수형 변수 선언 // 함수 종료 시 x는 자동 해제됨}1.2 힙 메모리 (Heap Memory)
- 관리 주체: 프로그래머 (명시적 할당 및 해제)
- 특징: 프로그램 실행 중 동적으로 크기가 결정되거나, 함수 반환 이후에도 데이터가 유지되어야 할 때 사용합니다. 프로그래머가 명시적으로 할당하고 해제해야 합니다.
- 선언 예시:
void func() { int *x = (int *) malloc(sizeof(int)); // 힙에 메모리 할당 요청 // ... free(x); // 사용 후 반드시 해제}2. malloc() 함수
힙 메모리를 할당할 때 사용하는 표준 라이브러리 함수입니다.
#include <stdlib.h>void *malloc(size_t size);- 인자: 할당받을 메모리의 크기(바이트 단위).
- 반환값: 성공 시 할당된 메모리의 시작 주소(
void *), 실패 시NULL.
사용 시 주의사항
sizeof()연산자 사용:malloc(sizeof(int))와 같이sizeof()연산자를 사용하여 타입의 크기를 정확하게 전달해야 합니다. 숫자를 직접 입력하는 것은 이식성을 떨어뜨립니다.- 문자열 할당: 문자열을 위한 공간을 할당할 때는 반드시 종료 문자(
\0)를 고려하여malloc(strlen(s) + 1)과 같이 1바이트를 더 할당해야 합니다.sizeof()는 포인터 자체의 크기만 반환하므로 문자열 길이를 구할 때는 적합하지 않습니다.
3. free() 함수
더 이상 사용하지 않는 힙 메모리를 운영체제에 반환하는 함수입니다.
void free(void *ptr);- 인자:
malloc()이 반환했던 포인터. - 특징: 해제할 메모리의 크기를 인자로 전달하지 않습니다. 메모리 할당 라이브러리가 내부적으로 해당 포인터가 가리키는 메모리 블록의 크기를 추적하고 있기 때문입니다.
4. 흔한 오류 (Common Errors)
메모리 관리는 오류가 발생하기 쉽습니다. 다음은 컴파일은 성공하지만 실행 시 문제를 일으키는 대표적인 오류 유형입니다.
4.1 메모리 할당 누락 (Forgetting To Allocate Memory)
포인터를 선언한 후 메모리를 할당하지 않고 데이터를 복사하려는 경우입니다. 이 경우 Segmentation Fault가 발생합니다.
char *src = "hello";char *dst; // 메모리 할당되지 않음strcpy(dst, src); // Segfault 발생4.2 메모리 공간 부족 (Not Allocating Enough Memory)
필요한 크기보다 적게 메모리를 할당하는 경우로, 버퍼 오버플로우(Buffer Overflow) 라고 합니다. 주로 문자열 종료 문자 공간을 계산하지 않았을 때 발생합니다.
char *src = "hello";char *dst = (char *) malloc(strlen(src)); // 종료 문자 공간 부족strcpy(dst, src); // 힙 영역 침범 발생4.3 초기화 누락 (Forgetting to Initialize)
malloc()은 메모리 공간만 확보할 뿐 데이터를 초기화하지 않습니다. 초기화하지 않은 메모리를 읽을 경우 예측할 수 없는 값(Garbage Value)을 읽게 됩니다(Uninitialized Read).
4.4 메모리 해제 누락 (Memory Leak)
할당한 메모리를 free()로 해제하지 않는 경우 **메모리 누수(Memory Leak)**가 발생합니다. 장시간 실행되는 서버 프로그램 등에서 메모리 누수가 누적되면 시스템 메모리가 고갈될 수 있습니다.
NOTE참고: 프로세스가 종료되면 운영체제가 해당 프로세스의 모든 메모리를 회수합니다. 따라서 단기 실행 프로그램에서는 치명적이지 않을 수 있으나, 항상 메모리를 해제하는 습관을 갖는 것이 권장됩니다.
4.5 해제된 메모리 사용 (Dangling Pointer)
이미 free()를 통해 반환된 메모리 주소에 접근하는 오류입니다. 프로그램이 충돌하거나, 해당 주소에 다른 데이터가 할당되었을 경우 데이터 무결성이 훼손될 수 있습니다.
4.6 중복 해제 (Double Free)
동일한 포인터에 대해 free()를 두 번 이상 호출하는 오류입니다. 메모리 할당 라이브러리의 관리 구조가 손상되어 크래시가 발생할 수 있습니다.
5. 운영체제의 지원 (Underlying OS Support)
malloc과 free는 시스템 콜이 아닌 C 라이브러리 함수입니다. 이 라이브러리는 운영체제의 시스템 콜을 사용하여 메모리를 관리합니다.
5.1 brk / sbrk
프로그램의 힙 영역 끝(Program Break) 위치를 변경하는 시스템 콜입니다. sbrk를 호출하여 힙의 크기를 늘리거나 줄일 수 있습니다. 현대 프로그래밍에서는 이를 직접 호출하지 않고 malloc을 사용합니다.
5.2 mmap
mmap() 시스템 콜을 사용하여 운영체제로부터 익명 메모리 페이지(Anonymous Memory Region)를 직접 할당받을 수 있습니다. malloc 구현체는 대용량 메모리를 할당할 때 brk 대신 mmap을 사용하기도 합니다.
6. 기타 함수
calloc(): 메모리를 할당함과 동시에 모든 비트를 0으로 초기화합니다.realloc(): 이미 할당된 메모리 블록의 크기를 변경합니다. 기존 데이터를 유지하며 더 큰 공간으로 확장하거나 축소할 때 사용합니다.
7. 요약 (Summary)
이번 장에서는 메모리 API와 관리 기법을 정리했습니다.
- 스택은 컴파일러가 관리하는 자동 메모리이며, 힙은 프로그래머가 관리하는 수동 메모리입니다.
malloc으로 메모리를 할당하고free로 해제합니다.- 메모리 누수, 버퍼 오버플로우, 초기화 누락 등의 오류는 프로그램의 안정성을 저해합니다.
gdb나valgrind같은 디버깅 및 분석 도구를 활용하여 메모리 관련 오류를 탐지할 수 있습니다.
다음 장부터는 운영체제가 이 가상 주소 공간을 물리 메모리에 어떻게 매핑하는지, 구체적인 주소 변환 메커니즘을 다룹니다.
8. 용어 정리
스택 메모리(Stack Memory): 함수 호출 시 자동 할당/해제되는 메모리 영역.힙 메모리(Heap Memory):malloc/free를 통해 명시적으로 관리되는 동적 메모리 영역.Segmentation Fault: 프로그램이 접근 권한이 없는 메모리 영역에 접근 시 발생하는 오류.Buffer Overflow: 할당된 메모리 범위를 벗어나 데이터를 기록하는 오류.Memory Leak: 할당된 메모리를 해제하지 않아 사용할 수 없는 메모리가 누적되는 현상.Dangling Pointer: 해제된 메모리를 가리키는 포인터.brk/sbrk: 프로세스의 데이터 세그먼트 크기를 조정하는 시스템 콜.
