<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이라고 한다. 둘이 대충 비슷해 보여서 헷갈리는 경우가 있는데, 헷갈리지 말자.
도대체 누가 이렇게 많은 섹션들을 만들어 놓았고, 어느 용도로 쓰는걸까? 그냥 전역 변수는 그들끼리 한 섹션에 두고, 나머지는 또 다른 섹션에 두고 심플하게 갈 수는 없었을까!