리눅스에서는 프로세스의 메모리를 크게 5가지의 세그먼트(Segment)로 구분한다.
세그먼트란, 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것이다.
크게 코드 세그먼트, 데이터 세그먼트, BSS 세그먼트, 힙 세그먼트, 스택 세그먼트로 구분
코드 세그먼트는 실행 가능한 기계 코드가 위치하는 영역으로 텍스트 세그먼트라고도 불린다.
프로그램이 동작하려면 코드를 실행할 수 있어야 하므로 이 세그먼트에는 읽기 권한과 실행 권한이 부여된다. 반면 쓰기 권한이 있으면 공격자가 악의적인 코드를 삽입하기가 쉬워지므로, 대부분의 현대 운영체제는 이 세그먼트에 쓰기 권한을 제거한다.
아래 main함수가 컴파일되면, 554889e5b8697a00005dc3라는 기계 코드로 변환되는데, 이 기계 코드가 코드 세그먼트에 위치하게 된다.
int main() { return 31337; }
데이터 세그먼트에는 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치하고, CPU가 이 세그먼트의 데이터를 읽을 수 있어야 하므로 읽기 권한이 부여된다.
data 세그먼트 | 전역변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터가 위치 |
rodata(read-only data) 세그먼트 | 전역으로 선언된 상수 |
int data_num = 31337; // data
char data_rwstr[] = "writable_data"; // data
const char data_rostr[] = "readonly_data"; // rodata
char *str_ptr = "readonly"; // str_ptr은 data, 문자열은 rodata
int main() { ... }
컴파일 시점에 값이 정해지지 않은 전역 변수가 위치하는 메모리 영역이다.
즉, 초기화가 되지 않은 전역변수이고, 프로그램이 시작될 때 모두 0으로 초기화가 된다.
이 세그먼트에는 읽기 권한 및 쓰기 권한이 부여된다.
프로세스의 스택이 위치하는 영역이다. 함수의 인자나 지역 변수와 같은 임시 변수들이 실행중에 여기에 저장된다.
스택 세그먼트는 스택 프레임(Stack Frame)이라는 단위로 사용된다. 스택 프레임은 함수가 호출될 때 생성되고, 반환될 때 해제된다. 그런데 프로그램의 전체 실행 흐름은 사용자의 입력을 비롯한 여러 요인에 영향을 받는다.
따라서, 어떤 프로세스가 실행될 때, 이 프로세스가 얼마 만큼의 스택 프레임을 사용하게 될 지를 미리 계산하는 것은 일반적으로 불가능하다. 그래서 운영체제는 프로세스를 시작할 때 작은 크기의 스택 세그먼트를 먼저 할당해주고, 부족해 질 때마다 이를 확장해준다. 스택이 확장될 때, 기존 주소보다 낮은 주소로 확장되기 때문에 ‘아래로 자란다'라는 표현을 종종 사용한다.
이 영역에는 CPU가 자유롭게 값을 읽고 쓸 수 있어야 하므로, 읽기와 쓰기 권한이 부여된다.
스택과 마찬가지로 실행중에 동적으로 할당될 수 있다. 일반적으로 읽기와 쓰기권한이 부여된다.
세그먼트 | 역할 | 일반적인 권한 | 사용 예 |
---|---|---|---|
코드 세그먼트 | 실행 가능한 코드가 저장된 영역 | 읽기, 실행 | main() 등의 함수 코드 |
데이터 세그먼트 | 초기화된 전역 변수 또는 상수가 위치하는 영역 | 읽기와 쓰기 또는 읽기 전용 | 초기화된 전역 변수, 전역 상수 |
BSS 세그먼트 | 초기화되지 않은 데이터가 위치하는 영역 | 읽기, 쓰기 | 초기화되지 않은 전역 변수 |
스택 세그먼트 | 임시 변수가 저장되는 영역 | 읽기, 쓰기 | 지역 변수, 함수의 인자 등 |
힙 세그먼트 | 실행중에 동적으로 사용되는 영역 | 읽기, 쓰기 | 동적으로 할당 받은 메모리 |