[리눅스/linux]메모리 구조
세그먼트(Segment)
리눅스에서는 프로세스의 메모리를 5가지로 구분함. 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것인데, 크게 코드 세그먼트, 데이터 세그먼트, BSS 세그먼트, 힙 세그먼트, 스택 세그먼트로 구분한다. 메모리를 용도별로 나누면 각 용도에 맞게 적절한 권한을 부여할 수 있다는 장점이 있다. 권한은 읽기, 쓰기, 실행 3가지가 존재하고 CPU는 메모리에 대해 권한이 부여된 행위만 할 수 있다.
예를 들어 CPU는 데이터 세그먼트의 데이터를 읽을 수 있어야 하기 때문에 이 영역에는 읽기 권한이 부여됨. 반면 실행 대상이 아니므로 실행 권한은 부여되지 않음.
코드 세그먼트(code secment)
실행 가능한 기계 코드가 위치하는 영역. 텍스트 세그먼트라고도 불림. 프로그램이 동작하려면 코드를 실행할 수 있어야 하므로 이 세그먼트에는 읽기 권한과 실행 권한이 부여됨.
쓰기 권한이 있으면 공격자가 악의적인 코드를 삽입하기 쉬워지므로 대부분의 현대 운영체제는 이 세그먼트에 쓰기 권한을 제거함. 정수 31337을 반환하는 main함수가 컴파일 되면 554889e5b8697a00005dc3라는 기계 코드로 변환되는데, 이 기계 코드가 코드 세그먼트에 위치하게 됨.
데이터 세그먼트(data segment)
컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치함.
CPU가 이 세그먼트의 데이터를 읽을 수 있어야하므로 읽기 권한이 부여됨.
- 쓰기가 가능한 세그먼트 : 전역변수와 같이 프로그램이 실행되면서 값이 변할 수 있는 데이터들이 위치함. (⇒Data Segment)
- 쓰기가 불가능한 세그먼트 : 프로그램이 실행되면서 값이 변하면 안 되는 데이터들이 위치함. 전역으로 선언된 상수가 여기에 포함. (⇒rodata(read-only data) segment)
int data_num = 31337; // data segment
char data_rwstr[] = "writable_data"; // data segment
const char data_rostr[] = "readonly_data"; // rodata
char *str_ptr = "readonly"; //str_ptr은 data, 문자열은 rodata
int main() {..}
BSS 세그먼트(Block Started By Symbol Segment)
컴파일 시점에 값이 정해지지 않은 전역변수가 위치하는 메모리 영역. 개발자가 선언만 하고 초기화 하지 않은 전역변수 등이 포함됨. 여기 영역은 프로그램이 시작될 때 모두 값이 0으로 초기화 됨. 따라서 C 코드를 작성할 때 초기화 되지 않은 전역변수의 값은 0이 됨.
여기에는 읽기 권한 및 쓰기 권한이 부여됨.
int bss_data;
int main()
{
printf("%d\n", bss_data); // 0
return 0;
}
Stack 세그먼트(Stack Segment)
프로세스의 스택이 위치하는 영역. 함수의 인자나 지역변수 같은 임시 변수들이 실행 중에 여기에 저장됨. 스택 세그먼트는 스택 프레임(stack frame)이라는 단위로 사용됨. 스택 프레임은 함수가 호출될 때 생성되고, 반환될 때 해제됨. 그런데 프로그램의 전체 실행 흐름은 사용자의 입력을 비롯한 여러 요인에 영향을 받음.
void func()
{
int choice = 0;
scanf("%d", &choice);
if(choice)
call_true();
else
call_false();
return 0;
}
→ 사용자의 입력값(choice)에 따라 call_true / call_false 가 호출될 수 있음.
→ 지역변수 choice 가 스택에 저장.
따라서 어떤 프로세스가 실행될 때 이 프로세스가 얼마만큼의 스택 프레임을 사용하게 될지 미리 계산하기 어려움. 운영체제는 프로세스를 시작할 때 작은 크기의 스택 세그먼트를 먼저 할당해주고, 부족해질 때마다 이를 확장해줌.
스택에 대해 아래로 자란다는 표현을 하는 이유는, 스택이 확장될 때 기존 주소보다 낮은 주소로 확장되기 때문. 이 영역에는 CPU가 자유롭게 값을 읽고 쓸 수 있어야 하므로 읽기와 쓰기 권한이 부여됨.
Heap 세그먼트(Heap Segment)
힙 데이터가 위치하는 세그먼트. 스택과 동일하게 실행 중에 동적으로 할당될 수 있으며, 리눅스에서는 스택 세그먼트와 반대 방향으로 자람. C언어에서 malloc(), calloc() 등을 호출해서 할당받는 메모리가 이 세그먼트에 위치하게 되며 일반적으로 읽기와 쓰기 권한이 부여됨.
int main()
{
int *heapDataPtr = malloc(sizeof(*heapDataPtr)); // 동적 할당한 힙 영역
*heapDataPtr = 31337; // 힙 영역에 값을 씀
printf("%d\n", *heapDataPtr); // 힙 영역의 값을 사용함.
return 0;
}
만약 힙 세그먼트와 스택 세그먼트가 동일한 방향으로 자라면, 기존의 힙 세그먼트를 모두 사용하고 나면 이를 확장하는 과정에서 스택 세그먼트와 충돌하게 됨. 이를 쉽게 해결하기 위해 스택을 메모리의 끝에 위치 시키고 힙은 반대로 자라나게 함.
Leave a comment