나만의 작은 도서관

[OS] 프로세스의 메모리 구조 본문

Common/CS-일반

[OS] 프로세스의 메모리 구조

pledge24 2025. 2. 19. 02:06

프로세스의 메모리 구조

프로세스 메모리 구조

 

프로세스 메모리는 데이터의 종류에 따라 서로 다른 영역에 저장된다. 메모리 주소가 높은 것부터 순서대로 Stack, Heap, Data Segment, Read-Only Data, Text Segment(또는 Code Segment)로 구분된다.

 


1. 스택(Stack)

스택(stack) 메모리 영역은 함수 호출(function call)과 관련된 데이터(지역 변수, 매개변수 등)를 저장하는 메모리 공간이다.

// 매개 변수(a, c), 지역 변수(num)등이 스택 메모리 영역에 저장된다.
void func1 (int a, char c) 
{
  long long num = 10;
}

 

가장 높은 위치의 메모리 영역

프로세스 메모리 종류 중 가장 높은 주소에 위치한 메모리이다. 스택은 높은 주소 -> 낮은 주소 방향으로 쌓이며, 이를 "스택이 자란다"라고 표현하기도 한다.

 

스택 프레임(stack frame)

스택 프레임 예시

 

스택 프레임은 함수가 호출될 때 스택(Stack) 메모리에 생성되는 메모리 블록으로, 함수에 필요한 데이터를 저장하는 단위이다.

 

함수를 호출할때마다 새로운 스택 프레임이 생성된다. 스택 프레임의 크기는 컴파일 타임에 결정되며, [매개변수 -> 리턴 주소 -> 지역 변수] 순으로 함수에 필요한 데이터가 저장되어 스택 메모리 위에 쌓이게된다.

 

함수를 리턴하면 1) 리턴 값을 저장하고, 2) 쌓았던 스택 프레임을 제거한 다음(스택 포인터 갱신), 3) 스택 프레임에 저장되어 있던 반환 주소값을 통해 함수를 호출한 위치로 복귀한다.

 

스택 오버플로우(stack overflow)

연속적인 함수 호출로 인해 스택 프레임이 계속해서 쌓이기만 한다면, 어느 순간 정해진 스택 메모리 크기를 넘어서면서까지 스택 프레임을 쌓으려는 시도가 발생하게 되는데, 이를 "스택 오버플로우"라고 부른다. 일반적으로 재귀 함수로 인해 무한히 호출되는 경우 발생한다.

 

멀티 쓰레드 환경에서의 스택

각 쓰레드는 독립적인 스택 메모리를 가진다. 따라서, 멀티 쓰레드 환경에서는 쓰레드 개수만큼 스택 메모리 영역이 존재하게 된다. 


2. 힙(Heap)

힙 메모리 영역은 동적 할당(malloc, new)한 데이터를 저장하는 메모리 공간이다. 

int *heap_var = new int(10); // Heap 영역

 

힙 메모리 영역의 특징

  • 메모리 주소 값에 의해서만 참조되고 사용되는 영역. 런타임에 크기가 결정된다.
  • 할당한 메모리는 자동으로 해제되지 않는다. 할당 후 해제하지 않는다면 메모리 누수(memory leak)가 발생한다.
  • 힙 메모리 영역은 정해진 크기 없이 조절해서 사용할 수 있다. 따라서, 운영체제가 허용하는 한도 내에서 크기를 키울 수 있기 때문에 크기가 매우 크다.
  • 메모리 접근 시, 운영체제(OS)와 커널을 거치므로 스택에 비해 속도가 느리다.
  • 힙 메모리 영역은 공유 메모리이다. 따라서, 모든 쓰레드가 접근할 수 있기 때문에 멀티 쓰레드 환경에서 사용할때 주의해야한다.

3. 데이터 영역(Data Segment)

데이터 영역은 프로그램 종료될 때까지 유지되는 전역 변수와 정적 변수가 저장되는 메모리 공간이다. 

// 전역 변수와 정적 변수 (초기화됨) → .data 영역
int global_var = 42; 
static int init_var = 28;

// 전역 변수와 정적 변수 (초기화되지 않음) → .bss 영역
int uninitialized_global; 
static int uninit_var;

 

 

데이터 메모리 영역의 특징

  • 프로그램 시작 시 할당되며, 프로그램 종료 시까지 유지된다.
  • 초기화 유무에 따라 . data와 .bss영역에 구분되어 저장된다. .bss 영역에는 초기화되지 않은 전역 변수와 정적 변수가, .data 영역에는 초기화된 전역 변수와 정적 변수가 저장되어있다.
  • .bss 영역에 있는 변수들은 프로그램 실행 시 자동으로 기본값으로 초기화 된다.

 

data 영역과 bss 영역으로 구분되는 이유

초기화 유무에 따라 data 영역과 bss 영역으로 구분하여 관리하는 이유는 디스크 공간을 효율적으로 사용하기 위함이다. 초기화 되지 않은 변수들은 "저장해야할 값이 없기 때문에" 프로그램 실행 전까지 디스크에 저장해둘 필요가 없다.

따라서, 초기화 되지 않은 변수들은 .bss 영역에 따로 빼 둔 다음, 프로그램이 실행될 때 메모리(RAM)에 0으로 초기화함으로써 실행 파일 용량을 줄일 수 있다. 이 방법으로 컴퓨터는 1) 메모리 절약, 2) 디스크 -> RAM 복사 비용 감소 효과를 얻을 수 있다.

 

+) .bss 영역은 프로그램 실행 전까지 물리적인 공간을 차지하지 않는다. 대신, 실행 파일에 BSS 크기 정보를 저장해두고, 운영체제가 프로그램 실행 시, BSS 크기만큼 0으로 밀린 메모리를 RAM에 할당한다.


4. 읽기 전용 메모리 영역(Read-Only Data)

읽기 전용 메모리 영역은 프로그램에서 변경할 수 없는 상수 데이터(문자열 리터럴, const 변수 등)가 저장되는 메모리 공간이다. 

char *rodata_string = "Hello, World!"; // 문자열 리터럴 (Read-Only Data / .rodata)

 

읽기 전용 메모리 영역의 특징

  • .rodata에 저장된다.
  • 해당 영역에 저장된 변수들은 일반 변수와 다르게 수정할 수 없다(읽기 전용)

 

const가 붙었다고 항상 .rodata에 저장되지는 않는다.

컴파일러가 상황에 따라 const 변수를 사용하지 않는 방향으로 최적화를 할 수도 있기 때문에 const 변수들이 반드시 .rodata에 저장된다고 보장할 수는 없다.


 

5. 코드 영역(Text Segment 또는 Code Segment)

코드 영역은 프로그램의 실행 코드(기계어)가 저장되는 메모리 공간이다. 컴파일된 함수, 메서드, 명령어(instruction) 등이 들어있다.

void func() {
    // 코드 영역 (Text Segment)에 위치한 함수
}

 

 

코드 메모리 영역의 특징

  • 읽기 전용(RO, Read-Only) 속성을 가진다. → 실행 중 수정 불가 (코드를 변경할 수 없음)
  • 실행 파일이 로드될 때 고정된 주소에 배치된다.
  • 함수 코드가 저장되므로 함수 포인터를 통해 주소를 확인하면 코드 영역 주소를 확인할 수 있다.
  • 코드 영역은 공유 가능한 메모리 영역이다.

주소로 보는 프로세스 구조 예제

#include <iostream>

using namespace std;

int global_var = 42; // 전역 변수 (초기화됨) → .data 영역
int uninitialized_global; // 전역 변수 (초기화되지 않음) → .bss 영역

void func() {
    // 코드 영역 (Text Segment)에 위치한 함수
}

int main() {
    
    int local_var = 10; // Stack 영역

    int* heap_var = new int(10); // Heap 영역

    const char* str = "Hello, Read-Only Data!";  // 문자열 리터럴(.rodata)

    cout << "스택 영역 : "<< &local_var << '\n';
    cout << "힙 영역 : "<< (void*)heap_var << '\n';
    cout << "데이터 영역(.bss) : "<< &uninitialized_global << '\n';
    cout << "데이터 영역(.data) : "<< &global_var << '\n';
    cout << "읽기 전용 영역(.rodata) : "<< (void*)str << '\n';
    cout << "코드 영역(.code) : "<< (void*)func << '\n';

    return 0;
}

 

실행 결과

(일부 메모리 주소는 OS가 보안 강화를 위해 의해 ASLR을 적용하여 예상치 못한 주소가 나올 수 있다. 아래 결과에선 힙 메모리 영역과 읽기 전용 영역이 다르게 나왔다.)

스택 영역 : 0x61fe0c
힙 영역 : 0xe11c40
데이터 영역(.bss) : 0x407030
데이터 영역(.data) : 0x403010
읽기 전용 영역(.rodata) : 0x404001
코드 영역(.code) : 0x401550

 


참고 자료

https://m.blog.naver.com/techref/222270185816?recommendTrackingCode=2

https://blog.naver.com/techref/222274484731

https://velog.io/@cchloe2311/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0

https://modoocode.com/83

https://www.tcpschool.com/c/c_memory_stackframe