[운영체제] 프로세스 주소 공간

xoey·2024년 10월 24일

운영체제

목록 보기
3/15
post-thumbnail

서문

프로그램이 실행되면 운영체제는 프로세스에 필요한 자원들을 할당해 준다. 그중 가장 중요한 자원 중 하나가 메모리이다. 프로세스는 할당된 메모리를 효율적으로 사용하기 위해 일정한 구조로 메모리 공간을 나누어 관리하는데, 이를 프로세스 주소 공간이라고 한다. 주소 공간은 각 영역별로 서로 다른 역할을 가지고 있으며, 프로세스가 어떻게 자원을 활용하고 관리하는지를 이해하는 핵심 요소이다.

이 글에서는 프로세스 주소 공간의 구성 요소인 Stack, Heap, Data, Text(Code) 영역을 하나씩 살펴보고, 이들 간의 상호작용이 프로세스와 스레드의 동작에 어떤 영향을 미치는지 알아볼 것이다. 이를 통해 메모리 관리의 효율성과 효율적인 메모리 사용을 위한 운영체제의 다양한 방법론에 대한 이해를 높이고자 한다.

1. 프로세스 주소 공간

프로세스는 운영체제가 자원을 할당하는 단위이다.

프로세스가 메모리를 할당 받으면, 자신만의 방법으로 메모리를 관리하기 위해 이 공간들을 어떠한 구조로 관리하는데, 이를 프로세스 주소 공간이라고 부른다.

메모리는 한정되어 있기 때문에, 프로세스는 다양한 방법으로 메모리를 절약하려고 시도한다.

1.1. Stack

  • 함수의 호출과 관계되는 지역 변수와 매개 변수가 저장되는 영역
  • Stack 영역의 값은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다.
  • 메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다.
  • 재귀 함수가 너무 깊게 호출되거나 함수가 지역 변수를 너무 많이 가지고 있어 stack 영역을 초과하면 stack overflow 에러가 발생한다.

1.2. Heap

  • 런타임에 크기가 결정되는 영역
  • 사용자에 의해 공간이 동적으로 할당 및 해제되기 때문에 프로그램 실행 중 필요에 따라 커지고 작아질 수 있다.
  • 주로 참조형 데이터(e.g. 클래스) 등의 데이터가 할당된다.
  • 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.

1.3. Data

  • 전역 변수나 Static 변수 등 프로그램이 사용할 수 있는 데이터를 저장하는 영역
  • 어떤 프로그램에 전역/Static 변수를 참조하는 코드가 존재한다면, 이 프로그램은 컴파일된 후에 data 영역을 참조하게 된다.
  • 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸한다.
  • 단, 초기화되지 않은 변수가 존재한다면, 이는 BSS 영역에 저장된다.

1.4. Text(Code)

  • 프로그램이 실행될 수 있도록 CPU가 해석 가능한 기계어 코드가 저장되어 있는 공간
  • 프로그램이 수정되면 안 되므로 ReadOnly 상태로 저장되어 있다.

🔖 Data 영역과 Stack 영역을 구분하는 이유

Heap과 Code 영역은 완전히 다른 역할을 하는데, Data와 Stack 영역을 굳이 구분하는 이유는 뭘까?

가장 큰 이유는 역할 분배이다.

Stack 영역을 통해 함수의 흐름을 관리하고, Data 영역(+BSS 영역)을 통해 전역 변수, static 변수를 관리한다. 만약 한 프로세스가 여러 개의 스레드를 갖는다면, 각각의 스레드는 자신만의 Stack 영역을 갖는다. 이는 스레드 내에서 수행되는 함수의 흐름을 각각 관리하기 위함이다.

또 다른 중요한 이유는 Data 영역의 공유를 위해서이다.

각각의 스레드는 Stack 영역을 갖고 Data 영역은 공유한다. 즉, Data 영역의 동일한 내용을 공유함으로써 각각의 스레드는 똑같은 공간을 여러 개 만들지 않고 메모리를 절약할 수 있다. (이는 Code 영역에서도 마찬가지다.)

🔖 Heap, Stack 크기에 대한 고찰

Heap과 Stack은 같은 메모리 공간을 공유한다.
같은 메모리 공간이지만 Heap 영역은 낮은 메모리 주소부터 할당받고, Stack 영역은 높은 메모리 주소부터 할당받는다. 즉, 같은 물리적 공간에서 서로 다른 방향으로 확장된다.

Heap의 크기는 -Xmx4096m와 같이 설정할 수 있다. 이 값은 프로그램이 사용할 수 있는 최대 Heap 메모리 크기를 지정하는 것이다. 이 크기는 프로그램 실행 중에 점점 커질 수 있으며, 필요에 따라 메모리를 동적으로 할당받는다.

반면 Stack의 크기는 고정되어 있다. Stack의 크기는 메모리가 부족할 때 자동으로 늘어나지 않으며, 초과하면 “Stack Overflow” 오류가 발생한다. UNIX 계열에서 ulimit -s 명령어를 통해 크기를 확인할 수 있으며, 기본적으로 8MB 정도로 설정되어 있는 경우가 많다.

Stack 영역은 생성과 동시에 크기가 정해진다. 즉, 크기가 한 번 정해지면 바뀌지 않기 때문에, Heap 영역과 상관 없이 크기의 제한을 갖는다. 즉, 우리가 자주 볼 수 있는 Stack Overflow 같은 문제는, Heap 영역을 침범해서가 아니라 정해진 Stack 영역의 크기를 초과해서 발생한 문제라고 볼 수 있다.

🔖 스레드가 여러 개 있다면?

프로세스가 자원을 할당 받지만, 스레드도 자신만의 자원을 갖고 있어야 한다. 따라서 스레드도 자신만의 주소 공간을 갖고 있으나 약간의 차이가 있다.

다음 그림과 같이 실제로 각 스레드가 갖고 있는 것은 Stack 영역 밖에 없다.
즉, 나머지 공간은 프로세스의 값을 함께 쓰고 있고, 다른 스레드와 공유한다고 볼 수 있다.

이때문에 Data 영역에 있는 자원은 동시에 여러 스레드가 접근할 수 있고, 동기화 문제가 발생할 수 있다.

마치며

이번 글을 작성하며 프로세스의 메모리 구조를 구체적으로 이해할 수 있게 되었다. 코드를 작성하면서 가끔 겪었던 스택 오버플로우가 왜 발생하는지, 선언한 변수와 동적 할당된 데이터들이 각각 어디에 저장되는지 알게 되었다. 특히 Stack과 Heap이 같은 메모리 공간을 서로 다른 방향으로 확장하는 점은 처음 알게 되어 더욱 의미있었다.

이번 학습을 통해 프로그램을 작성할 때 메모리 사용에 좀 더 신경 쓰며, 복잡한 작업을 처리할 때 메모리 관리 방식을 최적화하는 방법에 대해 꾸준히 고민할 필요가 있음을 느꼈다.


Reference

profile
[Roman 8:18] consider that our present sufferings are not worth comparing with the glory that will be revealed in us.

0개의 댓글