<aside> 💡

전역 변수는 메모리에 어느 부분에 생성되는걸까? 우리가 열심히 짠 코드가 들어갈 __TEXT 섹션일까, 혹은 __DATA 섹션일까? 아래 C 코드를 보고 예측이 되는지 확인 해 보자.

</aside>

#include <stdio.h>

int init[100] = { 0, 1, 2 };
long noinit[1024];

const int init_const[100] = { 0, 1, 2 };
const long noinit_const[1024];

static int s_init[100] = { 0, 1, 2 };
static long s_noinit[1024];

static const int s_init_const[100] = { 0, 1, 2 };
static const long s_noinit_const[1024];

int main(void) {
    int scoped[100] = { 0, 1, 2, 1284, 474736 };
    int scoped_noinit[100];

    const int scoped_const[100] = { 3, 8, 2 };
    const int scoped_const_noinit[100];

    static int s_scoped[100] = { 34907, 473, 176, 104, 472 };
    static int s_scoped_noinit[100];

    static const int s_scope_dconst[100] = { 9, 13243, 3276 };
    static const int s_scoped_const_noinit[100];
}

(대충 너가 뭘 좋아할지 몰라서 다 준비해 봤어 짤)

열 여섯 가지의 종류로 골고루 준비해 봤다.

이 4개의 모든 조합을 다 실험해 봤다.

메모리는 여러 섹션으로 나눠져 있다. 우리가 열심히 짠 코드가 들어갈 __TEXT 섹션, 그리고 데이터가 들어갈 __DATA 섹션이 크게 보면 있다.

가상 메모리 주소

우선 프로세스가 쓰는 메모리 주소는 가상 메모리 (VM address, virtual memory address)라고 한다. 왜 가상이냐면 실제 메모리 주소가 아니라 이 프로세스한테만 적용되는 주소이기 때문이다.

그러니까 이 프로세서의 메모리 주소 4000이 다른 프로세스의 메모리 주소 4000과 같아 보일지라도, 실제 메모리 주소는 아주 다르다는 것이다! 이렇게 하여 한 프로세스가 다른 프로세스의 메모리를 건들거나 훔쳐볼 생각조차 하지 못하게 하는 것이다.

(여담으로 이 가상 메모리 주소와 실제 메모리 주소의 변환 과정은 꽤나 복잡한데, 이걸 변환해 주는 전용 하드웨어가 cpu에 존재할 정도다.)

가상 메모리 구조

그래서 가상 메모리의 어디에 들어갈까? 잠깐 멈춰서 아래 부분 중 각 변수들이 어디에 들어갈지 예측 해 보라.

0x0: __PAGEZERO
0x1'0000'0000: __TEXT
	0x1'0000'3c90: __text
	0x1'0000'3ddc: __stubs
	0x1'0000'3e00: __const
	0x1'0000'3fa0: __cstring
	
0x1'0000'4000: __DATA_CONST
	0x1'0000'4000: __got
	
0x1'0000'8000: __DATA
	0x1'0000'8000: __data
	0x1'0000'84b0: __bss
	0x1'0000'a640: __common

0x1'0001'0000: __LINKEDIT

0x1'6fdf'ef00: main 함수의 stack pointer

보면 모두 대문자로 된 부분은 세그먼트segment라고 하고, 소문자로 된 부분은 섹션section이라고 한다. 둘이 대충 비슷해 보여서 헷갈리는 경우가 있는데, 헷갈리지 말자.

도대체 누가 이렇게 많은 섹션들을 만들어 놓았고, 어느 용도로 쓰는걸까? 그냥 전역 변수는 그들끼리 한 섹션에 두고, 나머지는 또 다른 섹션에 두고 심플하게 갈 수는 없었을까!