현재 주류 메인메모리를 구성하는 DRAM은 각 cell이 capacitor로 구성되어 있습니다.
전원이 공급되지 않으면 결국 전하가 다 빠져나와서 전압이 떨어집니다. 그런 상태를 일반적으로 0으로 놓죠.
결국 컴퓨터에 전원공급이 이제 막 시작하는 시점에서는 0으로 채워져 있다고 볼 수도 있겠습니다.
물론 어플리케이션 수준에서 초기화되지 않은 지역변수가 쓰레기값을 가지는 것과는 전혀 다른 문제입니다.
유저 레벨의 프로그램은 바로 물리적 메모리(physical memory)를 땡겨오는 것이 아닙니다. OS가 할당해준 가상 메모리 4G 영역을 받습니다. 할당받은 가상 메모리는 어떤 것으로 채워져 있던지 그 내용을 OS가 보장해 주지 않습니다.
int a가 global에 있는지 local 에 있는지에 따라서 초기값이 달라 집니다. global에 있으면 OS는 이것을 초기에 0으로 채워 줍니다. local 변수라면 아무것도 해 주지 않습니다. 결론적으로는 로컬 변수는 stack에 있는 영역을 사용하는 것이기 때문에 이전 stack operation이 어떤 것이었느냐에 따라서 달라질 수도 있습니다.
일반 유저는 OS의 물리적 주소에 접근할 방법이 없습니다. 커널 프로그래밍을 하지 않으면요.
함수 내에서 int a; 를 하면(지역변수) x86 기준으로 현재 쓰는 대부분의 컴파일러들은
스택 포인터를 조정(빼기)해서 스택에 공간을 만듭니다. 따라서 질문자님이 보시는 쓰레기값이란
기존에 해당 스택 영역에 다른 값이 이미 써져있을 때 나오는 값입니다.
함수 콜을 하고 리턴을 하게 되면, 일반적으로 스택 포인터는 변화가 없습니다.
즉 리턴할 때 스택 포인터를 정리(epilogue)하기 때문에, caller 입장에서 보면 그대로입니다.
그렇지만 스택 포인터만 조정할 뿐이지, 스택 내의 값들을 별도로 초기화하거나 하지는 않습니다.
또한 질문자님이 main() 함수를 만들어서 그 안에 int a; 를 했다고 가정할 때
프로그램 시작이 main() 인데 왜 기존에 스택값이 써져있나요 라고 의문을 가지신다면,
보통 컴파일러는 메인 함수를 호출하기 전에 미리 다른 엔트리 함수에서 시작하도록 컴파일합니다.
윈도에서 프로그램을 디버깅해보면 main 부터가 아닌 다른 곳에서 시작해서
나중에 main 을 인자와 함께 호출해주는 것을 볼 수 있고, gcc 에서도 __libc_start_main 이라는
라이브러리 함수가 그 역할을 하는 것을 볼 수 있습니다.
즉 main 함수가 호출되기 전에 이미 다른 많은 함수들이 호출되고 리턴되는 작업을
거쳤고, 그 과정에서 main 보다 지역변수 공간을 더 많이 사용한 함수가 있다면 스택에는
그 값이 그대로 쓰레기값으로 남게 되는 것입니다.
별 관계는 없는 얘기지만 이런 값을 제대로 초기화하지 않아서 취약점이 발생하기도 합니다.
위에서 "main 보다 지역변수 공간을 더 많이 사용한 함수" 라고 얘기한 데서 눈치채셨겠지만
이 말은 즉 main 에서 지역변수를 상당히 크게 잡으면 0으로 초기화된 영역을 얻는다는 얘기입니다.
이는 컴파일러마다 다르고, 리눅스의 경우 보통 __libc_start_main 이 시스템 libc 에 의존하므로
실행하는 환경에 따라 차이가 있을 수 있습니다. (물론 이건 거의 변하지는 않습니다만)
가령 main 에서 char a[100000]; 정도로 크게 잡아서 a[0] 부터 값을 출력해보면 보통 0을 얻을 수 있습니다.
물론 끝까지 가보면 쓰레기값이 나올 것입니다.
현재 주류 메인메모리를 구성하는 DRAM은 각
현재 주류 메인메모리를 구성하는 DRAM은 각 cell이 capacitor로 구성되어 있습니다.
전원이 공급되지 않으면 결국 전하가 다 빠져나와서 전압이 떨어집니다. 그런 상태를 일반적으로 0으로 놓죠.
결국 컴퓨터에 전원공급이 이제 막 시작하는 시점에서는 0으로 채워져 있다고 볼 수도 있겠습니다.
물론 어플리케이션 수준에서 초기화되지 않은 지역변수가 쓰레기값을 가지는 것과는 전혀 다른 문제입니다.
유저 레벨의 프로그램은 바로 물리적 메모리
유저 레벨의 프로그램은 바로 물리적 메모리(physical memory)를 땡겨오는 것이 아닙니다. OS가 할당해준 가상 메모리 4G 영역을 받습니다. 할당받은 가상 메모리는 어떤 것으로 채워져 있던지 그 내용을 OS가 보장해 주지 않습니다.
int a가 global에 있는지 local 에 있는지에 따라서 초기값이 달라 집니다. global에 있으면 OS는 이것을 초기에 0으로 채워 줍니다. local 변수라면 아무것도 해 주지 않습니다. 결론적으로는 로컬 변수는 stack에 있는 영역을 사용하는 것이기 때문에 이전 stack operation이 어떤 것이었느냐에 따라서 달라질 수도 있습니다.
일반 유저는 OS의 물리적 주소에 접근할 방법이 없습니다. 커널 프로그래밍을 하지 않으면요.
함수 내에서 int a; 를 하면(지역변수) x86
함수 내에서 int a; 를 하면(지역변수) x86 기준으로 현재 쓰는 대부분의 컴파일러들은
스택 포인터를 조정(빼기)해서 스택에 공간을 만듭니다. 따라서 질문자님이 보시는 쓰레기값이란
기존에 해당 스택 영역에 다른 값이 이미 써져있을 때 나오는 값입니다.
함수 콜을 하고 리턴을 하게 되면, 일반적으로 스택 포인터는 변화가 없습니다.
즉 리턴할 때 스택 포인터를 정리(epilogue)하기 때문에, caller 입장에서 보면 그대로입니다.
그렇지만 스택 포인터만 조정할 뿐이지, 스택 내의 값들을 별도로 초기화하거나 하지는 않습니다.
또한 질문자님이 main() 함수를 만들어서 그 안에 int a; 를 했다고 가정할 때
프로그램 시작이 main() 인데 왜 기존에 스택값이 써져있나요 라고 의문을 가지신다면,
보통 컴파일러는 메인 함수를 호출하기 전에 미리 다른 엔트리 함수에서 시작하도록 컴파일합니다.
윈도에서 프로그램을 디버깅해보면 main 부터가 아닌 다른 곳에서 시작해서
나중에 main 을 인자와 함께 호출해주는 것을 볼 수 있고, gcc 에서도 __libc_start_main 이라는
라이브러리 함수가 그 역할을 하는 것을 볼 수 있습니다.
즉 main 함수가 호출되기 전에 이미 다른 많은 함수들이 호출되고 리턴되는 작업을
거쳤고, 그 과정에서 main 보다 지역변수 공간을 더 많이 사용한 함수가 있다면 스택에는
그 값이 그대로 쓰레기값으로 남게 되는 것입니다.
별 관계는 없는 얘기지만 이런 값을 제대로 초기화하지 않아서 취약점이 발생하기도 합니다.
위에서 "main 보다 지역변수 공간을 더 많이 사용한 함수" 라고 얘기한 데서 눈치채셨겠지만
이 말은 즉 main 에서 지역변수를 상당히 크게 잡으면 0으로 초기화된 영역을 얻는다는 얘기입니다.
이는 컴파일러마다 다르고, 리눅스의 경우 보통 __libc_start_main 이 시스템 libc 에 의존하므로
실행하는 환경에 따라 차이가 있을 수 있습니다. (물론 이건 거의 변하지는 않습니다만)
가령 main 에서 char a[100000]; 정도로 크게 잡아서 a[0] 부터 값을 출력해보면 보통 0을 얻을 수 있습니다.
물론 끝까지 가보면 쓰레기값이 나올 것입니다.
댓글 달기