목차

1. 메모리 개념 & 구조

2. 메모리 가상화 개념 & 배경

3. 주소 변환(Address Translation)

4. 가상 메모리를 위한 OS의 개입

 


1. 메모리 개념 & 구조

개념

메모리란 CPU가 직접 접근 가능한 기억 장치이며, 디스크에 있던 프로그램이 적재되어 프로세스가 실행될 때 사용되는 하드웨어 자원이다.

즉, 프로세스 실행에 사용되는 기억장치라고 보면 된다. 우리가 개발하는 함수와 변수들이, 이 메모리에 저장되어 사용된다.

 

구조

메모리는 기능에 따라 크게  아래 4가지 영역으로 나뉘게 된다.

1. Code 영역

2. Data 영역

3. Heap 영역

4. Stack 영역

1. Code 영역

Code 영역은 실행할 프로세의 코드가 저장되는 영역이다. 컴파일 시에 함수 코드를 기계어로 변환하여 저장되며, 런타임 시 수정될 수 없다(Read-Only). Spring 이나 React로 실행을 한 후에 코드를 수정해도 반영이 되지 않는 이유이다(중간에 코드 수정을 하면 해당부분만 컴파일되도록 돕는 라이브러리도 존재한다).

 

2. Data 영역

Data 영역은 전역변수(global variable) 과 정적변수(static variable)이 저장되는 영역이다.  컴파일 시 영역의 크기가 결정되며, 프로그램 실행 전에 정적으로 할당된다. 

 

3. Heap 영역

개발자가 동적으로 메모리를 할당할 때 사용되는 영역이다. 예를 들어, List 등의 데이터는 런타임 시에 데이터가 추가되고 삭제되기 때문에 정적으로 메모리 크기를 예측할 수 없다. 이렇게 런타임시에 동적으로 할당되어야 하는 데이터 저장을 위해 사용되는 영역이 Heap 영역이다. 런타임시에 크기가 결정되며 낮은 주소에서 높은 주소로 메모리가 할당된다는 특징을 가진다.

 

4. Stack 영역

함수 호출 시 사용되는 지역변수, 매개변수, 리턴 값 등이 저장되는 영역이다. 각 함수가 호출될 때 마다 함수의 영역(스택 프레임)이 선입후출(FILO) 방식으로 할당되며, 함수가 종료되면 스택 프레임이 해제된다. 컴파일 시에 사용할 함수가 정해져 있기 때문에 Stack 영역 역시 컴파일 시 영역의 크기가 결정되며, 높은 주소에서 낮은 주소로 메모리가 할당된다는 특징을 가진다.

 

(TMI) 만약 재귀함수나 변수의 메모리가 매우 큰 경우 오버플로우가 발생할 수 있다(재귀의 경우, 함수는 하나인데 어디까지 스택 프레임이 쌓일지 알 수 없기 때문). 

 

 

실제 코드를 통한 예시

파이썬 코드와 메모리 구조

 

 

메모리 정리

메모리
정의 - 프로세스 실행을 위한 데이터가 저장되는 하드웨어 자원

구조
1. Code 영역 - 프로그램의 함수가 기계어로 변환되어 저장
2. Data 영역 - 전역변수 및 정적변수 저장
3. Heap 영역 - 동적 할당을 위한 영역. Runtime 할당. 낮은 주소 > 높은 주소 할당
4. Stack 영역 - 지역변수, 매개변수, 리턴값 등이 스택 프레임으로 할당. 높은 주소 > 낮은 주소 할당

 

 

2. 메모리 가상화 개념 & 배경

개념

여러 프로세스가 메모리를 동시에 사용하고 있으나, 각 프로세스는 자신만 메모리를 사용하고 있다는 착각을 하게 되는데, 이 기술이 메모리 가상화이다. 

 

 

배경

이 메모리 가상화는 왜 사용하는 것일까? 이유를 알기 위해 초기 메모리가 어떻게 사용되었는지 잠시 짚고 넘어가자.

 

초기 메모리의 경우, 프로세스는 실제 물리 주소를 찾아 접근하였다. 즉, 개발자들이 메모리의 물리주소를 직접 계산해서 코드를 작성해야했다는 것이다. 메모리의 실제 물리 주소를 생각하며 코딩을 한다고 생각해보면 얼마나 불편한지 알 수 있다.

 

이러한 불편함을 해결하기 위해 프로세스에 자신만의 깨끗한 메모리 공간(가상 메모리)을 사용하도록 하고, MMU 라는 하드웨어에 가상 메모리와 실제 메모리 주소를 변환시켰다. 이게 메모리 가상화이며, 각 프로세스는 가상 메모리를 사용함으로써 더 이상 메모리의 물리 주소를 신경쓰지 않아도 되었다.

 

 

메모리 가상화 개념 및 배경 정리

메모리 가상화
정의 - 각 프로세스가 자신만의 깨끗한 메모리를 가진다는 착각을 일으키는 기술
배경 - 물리 주소를 고려하여 개발하는 등의 불편함을 해결하기 위해 메모기 가상화 등장

 

 

3. 주소 변환(Address Translation)

MMU 라는 하드드웨어를 통해 주소를 변환한다고 했는데, 이 MMU란 무엇이며 어떻게 메모리 가상화를 지원하는지 알아보자.

 

MMU란

MMU(Memory Management Unit) 란 CPU 내부에 위치한 메모리 관리 장치이며, 주로 메모리 가상화를 위한 기능을 수행하는 하드웨어이다.  주소 변환, 페이지 테이블 관리, 페이지 교체, 메모리 보호 등의 기능을 통해 시스템의 성능과 안정성을 지원한다. CPU와 메모리 간 메모리 가상화를 지원하는 하드웨어 칩이라고 알아두자. 

주소 변환을 하는 MMU

 

 

주소 변환 방식

주소 변환을 위해 MMU 내부에서 base, limit 라는 이름의 레지스터를 활용한다. 

base 레지스터는 실제 물리 주소상에서의 시작 주소를 저장하며, limit 레지스터는 프로세스가 사용하는 메모리의 사이즈를 저장한다.

base 레지스터, limit 레지스터

 

base,limit 레지스터를 가지고 가상 메모리 주소를 실제 메모리 주소로 변환하며, 해당 프로세스의 접근 가능 영역이 정해진다. 

1. 실제 메모리 주소(x) = base + 가상 메모리 주소
2. base ≤ 실제 메모리 접근 영역 < base + limit

if x 가 범위 내에 존재하지 않는다면 -> trap 발생

 

 

예를 들어, base 레지스터= 15KB, limit 레지스터 = 16KB 이고, 프로세스가 접근할 메모리의 가상주소가 각각 4KB, 20KB 이라고 가정하자. 해당 프로세스의 메모리 영역과 접근할 실제 주소를 구해보면 아래와 같다.

1. 실제 메모리 주소(x) = base + 가상 메모리 주소
2. 15KB(base) ≤ 실제 메모리 접근 영역 < 31KB (base + limit)

프로세스 가상 주소 > 실제 물리 주소
1. 4KB >> 4KB + 15 KB(base) >> 19KB
2. 20KB >> 20KB + 15KB(base) >> 35KB

 

주소 변환을 했을 때 가상 주소인 4KB와 20KB는  각각 19KB, 35KB 로 변환된다. 19KB는 범위 내에 존재하기 때문에 정상적으로 처리되지만, 35KB는 외부 영역에 접근하기 때문에 trap이 발생한다.

 

 

주소 변환(Address Translation) 정리

1. 물리 주소(y)=가상 주소(x)+base
2. base ≤ 접근 가능한 영역 < base+limit
3. y가 접근 가능한 영역 외의 메모리에 접근 시도 -> trap 발생

 

 

4. 가상 메모리를 위한 OS 개입

지금까지 메모리 가상화와 주소 변환 방식을 배웠다. MMU 와 base, limit 레지스터라는 하드웨어 자원을 사용하여 주소 변환을 하는데, 이때 OS는 아무일도 하지 않을까? 

 

메모리 가상화를 위해 OS도 특정 작업을 수행한다. 주소 변환 과정에서 OS가 하는 일은 크게 아래 3가지이다.

1. 프로세스 생성 & 종료 시 메모리 관리
2. Context Switching 시 레지스터 관리
3. 예외 처리

 

각 항목에 대해 OS가 하는 일을 구체적으로 알아보자.

 

1. 프로세스 생성 & 종료 시 메모리 관리

프로세스 생성 시

OS는 프로세스가 사용할 메모리 공간을 찾아 프로세스에 할당한다. 이 때, 빈 공간에 대한 정보는 Free List 라는 연결 리스트로 관리가 되며 프로세스가 메모리 공간을 할당 받았으면, Free List 에서 제거된다.

 

프로세스 종료 시 

프로세스가 정상 종료 또는 에러로 인해 강제 종료 되었을 경우, 프로세스는 메모리를 회수한다. Free List에 회수한 주소 공간에 대한 정보를넣어주고, 해당 프로세스가 사용한 메모리를 회수한다.

 

2. Context Switching 시 레지스터 관리

OS는 Context Switching 을 할 때, 해당 프로세스의 base와 limit 레지스터의 값을 PCB(Process Control Block)에 저장한다. 이후 해당 프로세스가 실행될 때, base와 limit 레지스터에 값을 복원하여 해당 메모리 영역을 관리한다. 

 

(추가) PCB 에 저장된 base와 limit을 갱신하는 경우도 존재한다. 실행중(Running)이 아닐 때 커널모드를 통해 메모리 공간을 변경할 수 있으며, 새 위치로 메모리를 변경한 후 PCB 의 base,limit 값을 갱신한다. 

 

3. 예외 처리

특정 프로세스가 허가 되지 않은 메모리 영역에 접근한다거나 허가 되지 않은 커널 명령어를 사용하는 경우가 존재한다. 이때 CPU 에서 예외를 발생시키며, 이 예외를 받은 OS는 허가 되지 않은 행위를 한 프로세스를 종료시킨다. 

 

 

가상 메모리를 위한 OS 개입 정리

1. 프로세스 생성&종료
    생성: 사용 가능한 메모리 영역을 찾아서 할당 
    종료: 메모리를 회수  
2. Context Switcing
    프로세스의 base, limit 레지스터를 저장 및 복원 
3. 예외 처리
    허가되지 않은 메모리 영역 또는 허가 되지 않은 커널 명령어 사용시 프로세스 종료

 

 

 

[참고 링크]

Remzi H. Arpaci-Dusseau,Andrea C. Arpaci-dusseau 공저, 「운영체제 아주 쉬운 세가지 이야기」, 홍릉, 2020

https://velog.io/@chaemin/OS-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%83%81%ED%99%94-2

https://tcpschool.com/c/c_memory_structure

https://m.blog.naver.com/dilector/221787088491

 

+ Recent posts