OS에서 프로세스는 자원 소유권의 단위이다.
여기서 말하는 자원에는 여러 종류가 존재하는데, 프로그램 실행을 위한 명령어 집합이 포함될 코드 영역, 프로그램 수행 중 여러 값을 저장하는데 사용될 힙 영역, Last-In-First-Out 방식으로 함수 호출과 지역변수를 저장하는데 활용될 스택 영역, 초기화된 글로벌 변수와 정적 변수가 저장될 데이터 영역, 초기화 되지 않은 글로벌 변수와 정적 변수가 저장될 BSS 영역 이 모두 프로세스가 소유하는 자원에 해당한다.
초기화 여부로 데이터 영역과 BSS 영역 영역을 구분하는 이유가 뭘까?
가장 큰 이유는 BSS 영역을 구분하여 프로그램 바이너리 파일(실행파일)의 크기를 줄일 수 있다는 것이다. 바이너리 파일은 기본적으로 코드 영역, 데이터 영역, BSS 영역으로 이루어지며 컴파일을 통해 생성된다.
이 때 사실상 초기화 되어있지 않은 BSS 영역의 데이터들은 0의 값으로 채워지는데, 이를 실제 데이터로 실행파일에 포함하는 것은 비효율적이라는 것이다. 따라서 대부분의 OS에선 바이너리 파일에는 단순하게 BSS 영역의 크기가 어느정도인지만 포함하고, 프로그램이 실행될 때 해당하는 영역만큼을 0으로 채워 관리한다.
프로세스에 필요한 메모리 영역을 표현한 그림은 위와 같다. 그리고 프로세스는 가상 메모리 라는 개념을 도입하여 이를 관리한다. 가상 메모리가 왜 필요할까? 이를 알아보기 전에, 가상 메모리가 없었을 때 멀티 프로세스가 어떻게 관리 되었는지 살펴보자.
위 이미지는 여러개의 프로세스를 물리 메모리에서 직접 관리하는 형태를 보여준다. 어떤 문제가 있었을까? 가장 큰 문제는 프로세스 끼리 서로의 영역을 침범하기가 너무 쉽다는 것이다. 이는 자칫 시스템 전체가 붕괴할 수 있는 큰 이슈이다.
따라서, OS는 직접 프로세스에게 물리 메모리 공간을 제공하지 않고 가상 메모리 공간을 제공한다. 이를 통해 프로세스가 자신만의 공간을 사용한다고 착각하게 만들며, 실제 데이터는 주소 변환 메커니즘을 통해 물리 메모리에서 관리한다. 즉, 위 이미지를 기준으로 프로세스가 0KB 주소에 해당하는 명령어를 수행한다고 착각할 때, 실제로는 주소가 변환되어 320KB 에 해당하는 명령어를 수행하고 있는 것이다.
여기까지가 가상 메모리에 대한 전반적인 이해였다. 다음으로 여러가지 궁금한 점을 해결해 보려고 한다.
실제로 가상메모리로 사용되는 주소공간의 크기는 어느정도 될까?
32비트 머신에서는 최대로 표현할 수 있는 주소가 2^32 이기 때문에 최대 4GB 의 가상 메모리 공간을 가질 수 있다. 이에 비해 64bit 머신에서는 2^64의 최대 가상 메모리 공간을 가질 수 있는데, 이는 말도 안되게 큰 크기이다. 그래서 실제로 대부분의 64비트 시스템에서는 48bit 주소 체계를 사용한다. 이는 약 256TB 라고 볼 수 있는데, 이 또한 여전히 큰 크기다. 적어도 가상 메모리 최대 주소가 작아서 프로세스가 수행되지 못하는 일은 없을 것이다.
아니 그러면 프로세스가 수행된다면 가상메모리 공간을 256TB 나 가지고 있다고?
그건 아니다. 이는 최대치를 의미하는 거지 실제 프로세스는 자신이 필요한 적당한 만큼의 가상 메모리를 관리하게 될 것이다.
code, data, bss 영역등 미리 사이즈를 정해도 문제가 없겠지만, heap 이나 stack 영역은 그렇지 않을 것 같은데 어떻게 프로세스가 적당한 만큼의 가상 메모리를 판단할 수 있는거지?
뭔가 잘못 이해하고 있는 것 같은데, 프로세스가 정확한 heap, stack 영역의 사이즈를 미리 알고 있을 필요가 없다. heap 영역에서는 malloc() 이 호출될 때 그 때 알아서 새롭게 메모리가 할당 될 것이고, stack 영역도 마찬가지로 작은 사이즈에서 시작해서 process call 이 쌓이며, 메모리 공간이 필요해질 때 마다 새롭게 메모리가 할당 될 것이다.
물론 메모리가 늘어날 수 있는 한계는 존재한다. 256TB 를 쓸 수 있다고 그만큼 쌓일 수 있는게 아니다. (물리적으로 불가능 하다) 만약 그 한계를 넘어서게 메모리를 요청한다면 에러가 발생할 것이고, 그 에러가 heap overflow, stack overflow 인 것이다.
heap, stack 물론 당연히 여러 케이스별로 다르겠지만 size limit 이 어느정도 되는데?
크기 제한은 운영 체제, 하드웨어 및 시스템 구성 등에 다르고, 사용자 또는 시스템 관리자가 조정할 수도 있어 확실한 표준을 제시하긴 어렵다. 하지만, 일반적인 가이드라인은 제공이 가능하다.
먼저, heap 영역은 사실상 딱 정해져있는 제한은 없다. 즉 논리적으론 가상 메모리 공간의 크기만큼 메모리를 할당 받을 수 있을 것이다. 하지만 이는 물리적 메모리와 디스크의 한계에 의하여 제한될 것이다. 즉, 내 맥북이 1TB SSD 를 사용하는데, 사실상 이를 넘어서는 메모리 할당은 불가능 할 것이다.
stack 영역에는 보다 구체적인 제한이 존재한다. 리눅스에서는 기본적인 stack size limit 은 대략 8MB 정도이다. 하지만, ulimit 커맨드나 RLIMIT_STCK 등의 값을 설정하여 programtically 값을 변경할 수 있다고 한다. 윈도우의 경우, 기본적인 size limit 은 1MB 정도이며, 마찬가지로 링커 세팅 등을 통해 변경될 수 있다고 한다.
이와 같은 size limit 은 단일 프로세스가 너무 많은 메모리를 소비하여 시스템에서 실행 중인 다른 프로세스에 악영향을 미칠 수 있는 것을 방지하기 위해 마련되었다고 볼 수 있다.
'운영체제' 카테고리의 다른 글
Segmentation & Paging (0) | 2023.06.15 |
---|---|
Synchronization (0) | 2023.05.29 |
Scheduling (1) | 2023.05.13 |
IPC (Inter-Process Communication) (0) | 2023.04.24 |