메모리 관리란 무엇인가?
들어가기 앞서 메모리라는 개념을 알고 있을 필요가 있다.
메모리란 무엇인가?
메모리는 컴퓨터에 어떤 연산을 수행할 때 사용되는 데이터 주소 결과 값 등을 잠시 기억하기 위해 사용되는 일종의 기억장치이다.
장 좁은 의미로 "메인메모리"를 가리키며 전기적인 신호로 저장되기 때문에 속도가 매우 빨라 컴퓨터의 주기억장치(RAM)로 이용된다. 이 메모리는 각 프로세스마다 독립된 공간을 가지며 운영체제 또는 다른 프로세스 메모리 공간에 접근할 수 없다.
메모리의 영역
1. 실행되어야 하는 바이트코드를 로드한다.
코드 영역 (Code Segment / Text Segment)
- 프로그램의 실행 코드(기계어, 바이트코드 등)가 저장되는 영역입니다.
- 읽기 전용(일반적으로), 실행 가능.
- 예: main(), 함수 정의, if문, for문 내부 코드 등이 이 영역에 들어갑니다.
2. 실행되어야 하는 프로그램이 사용하는 데이터 값과 데이터 구조를 저장한다.
데이터 영역 + 힙 + 스택
이건 구체적으로 세 부분으로 나뉩니다:
1.데이터 영역 (Data Segment)
- 전역 변수, 정적 변수(static)가 저장됨.
- 프로그램 시작 시 메모리에 올라가고 종료까지 유지.
- 이들의 특징을 보면 보통 메인(main)함수 전(프로그램 실행 전)에 선언되어 프로그램이 끝날 때 까지 메모리에 남아있는 변수
- Data영역도 크게 두 가지로 나뉜다.
- 초기화 된 변수 영역(initialized data segment)과 초기화되지 않은 변수 영역(uninitialized data segment)
2. 힙 영역 (Heap Segment)
- 프로그래머가 동적으로 할당한 메모리 (C의 malloc(), JAVA new 등).
- 메모리 할당은 런타임 시에 결정되기 때문에 데이터의 크기가 확실하지 않을 때 사용한다
- 즉 프로그래머가 관리해야하는 메모리 영역
- 힙 영역에 메모리를 동적 할당 후 사용하고 난 후에는 반드시 메모리 해제를 해줘야 메모리 누수(memory leak)가 발생하지 않는다.
가비지 컬렉션(GC, Garbage Collection)
힙 영역에서 동적으로 할당했던 메모리 영역 중 필요없게 된 메모리 영역을 주기적으로 삭제하는 프로세스
C나 C++에는 가비지 컬렉션이 없어 프로그래머가 수동으로 메모리 할당과 해제를 일일이 해줘야하지만 Java는 JVM에 탑재되어있는 가비지 컬렉터가 메모리 관리를 대행해준다.
3. 스택 영역 (Stack Segment)
- 지역 변수, 함수 호출 시의 인자, 리턴 주소 등을 저장.
- 함수가 호출될 때마다 스택 프레임이 생성됨.
- 스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 끝나면 메모리에서 해제된다.
- 스택 영역의 메모리 할당은 컴파일 타임에 결정되기 때문에 무한히 할당할 수 없다.
- 스택은 push동작으로 데이터를 저장하고, pop동작으로 데이터를 추출하는 후입선출(LIFO, Last-In First-Out) 방식에 따라 동작하므로, 가장 늦게 저장된 데이터가 가장 먼저 추출된다.
=> 이게 무슨말이지? 스택 형식으로 어떻게 수행된다는건데???
원리
- 함수가 호출될 때: 스택에 정보가 push됨
- 함수가 끝나면: 그 정보들이 pop되어 사라짐
- 그래서 가장 나중에 호출된 함수가 가장 먼저 종료됨
이런 함수 호출이 있다고 예시를 들어보자
- main 함수가 호출되면 스택에 main의 스택 프레임이 생기고 그 안에 x라는 지역변수가 들어간다
- 그 이후 funcA 가 호출되면 스택안에 funcA의 스택 프레임이 쉬에 생기고 안에 a라는 지역변수가 들어간다
- 자 그러면 지금 제일 상위에 있는것은 a라는 지역변수이며 funcA 프레임이 가장 상위에 올라가있으니
- funcA가 먼저 수행이 된다 즉 이 작업이 pop이라고 보면 된다
- 그 다음에 main이 호출되며 마무리 지어진다
- 이 과정을 보면 이제 후입선출로 수행이되는구나! 라는걸 이해할 수 있다.
컴파일 시 메모리 영역의 크기가 결정되기 때문에 무한정 할당할 수 없다??
간단하다 애초에 스택영역은 운영체제가 크기를 결정해 놓는다
왜? => 지역변수, 함수 호출 정보, 리턴주소 등이 저장되는데 이게 계속 커지면 힙이나 데이터 영역을 침범해 시스템이 불안정 해질 수 있
만약
"스택은 운영체제에서 크기를 결정해놓잖아 만약 힙영역을 계속 채워서 스택의 최하위 주소랑 만나게되는 상황이 되면 어떻게 될까?"
위 그림에는 표현되어 있지 않지만 스택과 힙 사이에 guard page 라는 페이지와 보호구역이라는게 있어서 예방해준다.
하지만 여기서도 도전해서 충돌이 일어나면? 그게 우리가 익히 아는 스택 오버플로우 / 힙 오버플로우이다.
3. 프로그램 실행에 필요한 런타임 시스템을 로드한다.
라이브러리 영역 또는 공유 메모리 영역 등 (OS에 따라 다름)
- 예: C 런타임 라이브러리, JVM, 파이썬 인터프리터 등이 여기에 해당.
- 일반적으로 공유 라이브러리 영역 또는 커널 영역과 연결됩니다.
- OS는 이 런타임 코드도 메모리에 함께 로드하여 프로그램 실행을 도와줍니다.
자 그러면 메모리에 데이터가 적제되었다면 저 순서대로 데이터가 흐를까?
저건 말 그대로 메모리 주소 공간의 배치 즉 구조적 레이아웃일뿐 흐름은 저렇게 흘러가지 않는다
- CPU는 코드 영역의 명령어를 순서대로 실행 (main → printf 등)
- 실행 도중 지역 변수 local은 스택에 생성
- malloc() 호출 시, 운영체제가 힙에 공간을 마련
- 전역변수 global은 이미 데이터 영역에 올라감
- *ptr = 3을 통해 힙 메모리에 값을 저장
- printf()는 이 세 곳에 흩어져 있는 값을 읽어서 출력
즉, 실행 중 데이터는 스택, 힙, 데이터 영역, 코드 영역을 넘나들며 흩어져서 사용
→ 어떤 순서대로 흐르는 것이 아니라, 필요할 때마다 접근하는 구조
결국 저건 주소임! 주소임을 까먹지말자
메모리의 특징
기억장치는 지역성이라는 특성을 가진다. 이 지역성은 시간적 지역성과 공간적 지역성이 있다.
- 시간적 지역성: 한 번 참조되면 곧바로 다시 참조되기 쉬운 특성
- 공간적 지역성: 어떤 내용이 참조되면 그 내용에 가장 가까운 곳에 있는 다른 내용이 곧바로 참조되기 쉬운 특성
→ 그래서 데이터를 한 번 불러올 때 근처까지 묶어서 가져오고,
→ 자주 쓰는 건 더 빠른 캐시에 저장해두는 것
기억장치는 지역성의 원리를 이용하여 계층으로 구성된다.
순서는 레지스터(CPU), 캐시기억장치(CPU), 주기억장치(메모리), 보조기억장치(하드디스크)
메모리 계층 주소
메모리 계층 구조는 메모리 관련 3가지 주요 특성인 용량, 접근 속도 비용간의 절충 관계를 파악해 필요해 따라 채택 할 수 있게 나타낸 구조이다.
전체 기억장치를 구성하는데 있어서 가격은 최소화 하면서 가능한 빠른 접근 속도와 대용량의 크기를 제공하기 위해 만들어졌다
비싼 하드웨어는 꼭 필요한 만큼의 크기만 사용하고, 싼 하드웨어는 넉넉한 크기만큼 사용하기 때문에 메모리 계층 구조가 피라미드 모양으로 나타난다.
위 그림처럼 위로 갈수록 빠르고 아래로 갈수록 용량이 커진다
계층 구조를 설명하기 위해선 cpu가 데이터를 찾는 과정을 알고있어야한다
간단하게 말하면 cpu는 직접 디스크나 메모리 전체를 검색하는게 아니라 주소를 요청할 뿐이다.
레지스터
레지스터(Register)란, CPU 내부에서 CPU가 요청을 처리하는데 필요한 데이터를 일시적으로 저장하는 기억장치.
레지스터는 연산과 프로그램의 실행을 효율적으로 수행하기 위해 사용되며, 메모리(주로 RAM)로 연산의 결과를 보내고 영구적으로 저장할 데이터를 하드디스크에 저장하는 등의 명령을 처리하기 위한 주소와 명령의 종류를 저장하는 기억 공간의 역할을 한다.
레지스터의 핵심 목적은 데이터를 빠르게 CPU로 가져오는 것이다. 레지스터의 저장공간은 작지만, CPU 외부에 위치한 메모리와 다르게 CPU와 직접 연결되어 있어 연산 속도가 메모리보다 수십~수백 배까지 빠르다.
캐시
Cache Memory는 CPU와 메인 메모리 간의 데이터 속도 향상을 위한 중간 버퍼 역할을 해주는 CPU 내부에 존재하는 메모리
즉, 사용되었던 데이터는 다시 사용되어질 가능성이 높다는 개념을 이용하여, 다시 사용될 확률이 높은 것은 더 빠르게 접근할 수 있도록 하는 저장소를 사용한다는 개념.
또한, 빠른 장치와 느린 장치 사이의 속도 차이에 따른 병목 현상을 줄이기 위한 메모리이.
캐시는 L1 - L2 - L3 캐시 구조를 가진다
작동 흐름
- CPU가 어떤 데이터를 필요로 함
- 먼저 L1 캐시에서 찾음 → 있으면 L1 히트
- 없으면 L2 캐시에서 찾음 → 있으면 L2 히트
- 또 없으면 L3 캐시에서 찾음 → 있으면 L3 히트
- L1~L3 모두에 없다면, RAM에서 읽어옴 → 이게 캐시 미스
레지스터랑 캐시랑 둘다 cpu 내부에 있네? 뭐가 큰 차이야?
- 레지스터는 CPU 손에 들고 있는 도구이다 바로 연산가능.
- 캐시는 작업대 옆에 쌓인 서랍 같은 거야. RAM(창고)까지 갈 필요 없이 자주 쓰는 걸 미리 배치.
- RAM은 멀리 있는 창고
캐시의 지역성이란?
캐시메모리의 역할을 제대로 수행하기 위해서는 CPU가 어떤 데이터를 원할 것인가를 어느 정도 예측할 수 있어야 한다.
캐시의 성능은 작은 용량의 캐시 메모리에 CPU가 이후에 참조할, 쓸모 있는 정보가 어느 정도 들어있느냐에 따라 좌우되기 때문
이 때 적중율(Hit rate)를 극대화 시키기 위해 데이터 지역성(Locality)를 사용한다.
지역성의 전제조건으로 프로그램은 모든 코드나 데이터를 균등하게 Access 하지 않는다는 특성을 기본으로 한다.
즉, Locality란 기억 장치 내의 정보를 균일하게 Access 하는 것이 아닌 어느 한 순간에 특정 부분을 집중적으로 참조하는 특성
캐시 적중(Cache Hit) : CPU가 액세스하려는 데이터가 이미 캐시에 적재되어있는 상태.
캐시 미스(Cache Miss) : CPU가 액세스하려는 데이터가 캐시에 없어 주기억장치로부터 인출해 와야하는 상태.
캐시 적중률(Cache Hit rate) : CPU가 원하는 데이터가 캐시에 있을 확률
캐시에 적중되는 횟수 / 전체 기억장치 액세스 횟수
미스율(Miss rate) : CPU가 원하는 데이터가 캐시에 없을 확률
캐시 라인이란?
**캐시 라인(Cache Line)**이란, CPU 캐시에 데이터를 적재할 때 한 번에 가져오는 데이터의 최소 단위를 말한다.
- 보통 64바이트 크기(최근 CPU 기준)
- CPU는 메모리에서 데이터를 1바이트 단위로 가져오는 게 아니라, 한 줄(Cache Line) 단위로 미리 가져온다
- 이유: 공간 지역성(spatial locality) 때문
→ 가까운 주소끼리 같이 접근될 확률이 높기 때문이다
쉽게 생각하면 배열을 선언한 후 가져오면 쭉 한줄단위로 캐시에 로드된다.
근데 캐쉬랑 버퍼는 뭐가달라 비슷해 보이는데?
버퍼는 데이터를 한 곳에서 다른 곳으로 전송하는 동안, 일시적으로 데이터를 보관하는 메모리 공간을 말한다.
컴퓨터 시스템에서 데이터 송신 측과 수신 측의 속도가 서로 다를 때, 그 차이로 인해 데이터 손실이나 끊김 현상이 발생할 수 있다.
이런 경우 버퍼가 중간에서 속도를 조절하며 데이터를 임시로 저장함으로써, 안정적인 데이터 전송을 가능하게 한다.
- 인터넷으로 영상을 스트리밍할 때,
영상 데이터를 다운받는 속도와 재생 속도가 일치하지 않으면
중간에 영상이 멈추고 "버퍼링"이 발생한다. - 이는 플레이어가 미리 받아둔 영상 데이터가 다 소모되고,
다음 데이터를 기다리는 동안 발생하는 현상이다. - 여기서 영상 플레이어가 사용하는 메모리 공간이 바로 버퍼이다.
캐시는 CPU 속에 있고, 버퍼는 CPU 밖에 있다. 캐시는 속도 향상을 위한 읽기 중심 구조이고,
버퍼는 데이터 안정성과 흐름 제어를 위한 완충지대이다.
메인메모리
메인 메모리는 주기억장치라고도 불리며, 컴퓨터 시스템에서 가장 중요한 메모리 계층 중 하나이다. 메인 메모리는 프로그램 실행 중에 프로세서가 실제로 엑세스하는 데이터와 명령어를 저장하는 곳이다.
본문 제일 상단에 있는 메모리의 영역은 이 주기억장치 위에 존재한
RAM(Random Access Memory)
RAM은 컴퓨터 시스템에서 주로 사용되는 메모리 유형으로 데이터와 명령어를 읽고 쓸 수 있는 가용한 메모리 공간이다. CPU가 프로그램 실행 중에 실제로 엑세스하는 메모리로, 프로그램 실행에 필수적이다.
Random Access는 어느 위치에서든 똑같은 속도로 접근하여 읽고 쓸 수 있다는 것을 의미한다.
주요 RAM유형으로는 DRAM(Dynamic RAM)과 SRAM(Static RAM)이 있다. 동적 / 정적
ROM(Read Only Memory)
ROM은 읽기 전용 메모리로, 데이터를 읽을 수만 있고 수정할 수는 없는 메모리 유형이다.
ROM에 저장된 데이터는 제조과정에서 기록되면, 사용자가 변경할 수 없다.
ROM에 저장된 데이터는 영구적으로 보존되므로 전원이 꺼져도 데이터가 손실되지 않는다.(비휘발성)
주로 시스템의 초기화 및 부팅에 필요한 프로그램 코드, 펌웨어, 시스템 설정 등이 저장된다.
그러면 코드 관련된 내용들이 메인메모리에 들어가 있다면 레지스터와 캐시에는 어떤게 들어가있을까?
RAM에 있는 데이터를 CPU가 사용하려면 → 레지스터나 캐시로 먼저 복사해야 함
즉, 레지스터/캐시는 RAM의 "작고 빠른 사본" 역할
이 코드가 실행되면:
- a라는 변수는 RAM(스택 영역)에 저장
- CPU가 a를 연산에 쓰려면
→ RAM에서 a의 주소값을 읽어서
→ 먼저 캐시에 복사
→ 그리고 그 값을 다시 **레지스터(RAX 등)**에 복사 - 레지스터에서 +5 연산을 수행함
- 결과는 다시 RAM에 저장됨
그래서 레지스터와 캐시는 RAM에 있는 데이터를 "잠깐 들고 일하는 공간"
이걸 안 쓰면 생기는 문제
- 매번 RAM에서 데이터를 꺼내 쓰면 → CPU가 그 시간 동안 기다려야 함 (stall)
- 캐시/레지스터 덕분에 → CPU는 미리 꺼내온 값으로 빠르게 작업 가능
보조기억 장치
보조기억장치는 주기억장치인 RAM보다 용량이 크고, 데이터를 영구적으로 저장하는 역할을 수행하는 메모리 유형이다.
대용량 저장 공간: 보조기억장치는 주로 하드디스크 드라이브(HDD) 또는 고체 상태 드라이브(SSD)와 같은 외부 저장 장치로 구성된다. 큰 단위의 저장 공간을 제공하여 많은 양의 데이터를 저장할 수 있다.
영구 저장: 보조기억장치는 데이터를 영구적으로 저장한다. 전원이 꺼져도 데이터는 보조기억장치에 그대로 유지되며, 필요할 때 읽고 쓸 수 있다. 이러한 특성으로 인해 중요한 데이터의 백업이나 영구 보존이 필요한 경우에 사용된다.
상대적으로 느린 엑세스 속도: 보조기억장치는 주기억장치인 RAM에 비해 엑세스 속도가 상대적으로느리다. 하드디스크 드라이브의 경우 디스크 회전 및 헤드 이동과 같은 기계적 동작이 필요하기 때문에 상대적으로 더 많은 시간이 소요된다.
데이터 저장과 검색: 보조기억장치는 데이터 저장과 검색을 위한 파일 시스템이나 데이터베이스와 같은 소프트웨어 시스템과 함께 사용된다. 파일 시스템은 데이터를 구조화하고 관리하여 적잘한 위치에서 데이터를 검색하고 엑세스할 수 있도록 한다.
cpu는 보조기억 장치에 직접 접근하지 않는다!
보조 기억 장치에 있는 데이터를 RAM으로 가지고 올라온 후 사용해야한다
이 전체 과정을 운영체제가 관리하며, 가상 메모리(Virtual Memory) 시스템과 페이지(Page) 단위로 이루어집니다.
cpu는 필요한 데이터가 하드디스크에 있다면 특정적으로 거기 탐색해도 되지않나?
- 캐시 계층 활용
CPU와 메모리 간의 속도 차이를 줄이기 위해 캐시가 사용된다.
자주 사용하는 데이터를 캐시에 저장해 데이터 접근 시간을 줄이고 성능을 높인다. - 데이터 일관성과 보안
데이터는 메모리, 캐시, 디스크 등 다양한 계층에 복제되거나 암호화되어 저장된다.
이를 통해 데이터의 무결성과 보안, 일관성을 유지한다. - 성능 최적화
계층 구조를 통해 데이터를 효율적으로 배치하고 관리함으로써
저장장치 접근 지연을 줄이고 시스템 전체 성능을 최적화할 수 있다. - 추상화와 모듈화
계층 구조는 하드웨어와 소프트웨어 간의 역할을 분리하고 추상화해준다.
이를 통해 유지 보수성과 확장성이 향상된다.
즉 하드디스크로부터 데이터를 직접 가져오는 것이 빠른 것처럼 보일 수 있지만, 실제로는 데이터의 안전성, 보안, 일관성, 성능 최적화 등을 고려하여 다양한 계층 구조를 통해 데이터를 관리하는 것이 효과적이고 안전한 방법
메모리 관리 전략
배경
각각의 프로세스는 독립된 메모리 공간을 갖고, 운영체제 혹은 다른 프로세스의 메모리 공간에 접근할 수 없는 제한이 걸려있다. 단지, 운영체제 만이 운영체제 메모리 영역과 사용자 메모리 영역의 접근에 제약을 받지 않는다.
메인 메모리(Main Memory, Physical Memory, 주기억장치)는 CPU가 직접 접근할 수 있는 기억 장치로, 프로세스가 실행되려면 프로그램 코드를 메인 메모리에 적재해 두어야 한다. 그런데, 만약 프로그램 용량이 메인 메모리보다 크면 어떤 일이 벌어질까?
Swapping
swap이라는 단어는 두 값을 맞바꾸다, 교환하다는 의미를 가진다.
운영체제에서의 스와핑도 이와 비슷한 의미로 사용된다.
프로세스는 실행되기 위해 반드시 메인 메모리(RAM)에 적재되어야 한다.
하지만 메모리는 용량이 제한되어 있어, 동시에 실행 가능한 프로세스 수에도 제한이 있음.
스와핑은 실행 중인 프로세스를 메모리에서 보조기억장치로 잠시 옮겼다가, 다시 불러오는 작업을 의미한다.
예를 들어, 한 시스템에서 메모리는 최대 10개의 프로세스만을 동시에 실행할 수 있다고 가정하자.
이미 10개의 프로세스가 모두 메모리에 올라와 있는 상황에서 11번째 프로세스가 실행되면, 기존에 있는 프로세스 중 하나를 잠시 메모리에서 내보내야 한다.
이때 일반적으로는 가장 덜 사용 중인 프로세스(예: 이벤트를 오래 기다리고 있는 3번 프로세스)를 대상으로 삼는다.
이 프로세스를 완전히 종료하지 않고, 잠시 보조기억장치에 저장해두는 작업을 Swap-out이라 한다.
그리고 나중에 다시 해당 프로세스를 실행해야 할 때, 보조기억장치에서 메모리로 다시 불러오는 작업을 Swap-in이라 한다.
1시간이 지나고 방금 빼놨던 3번 프로세스에서 이벤트 요청이왔다.
그렇다면 다시 메모리로 복귀시켜줘야함 다시 메모리에 올려서 실행시키는 작업을 Swap-in이라고 하고, Swap-out, Swap-in 하는 과정을 스와핑(Swapping)이라고 한다.
스와핑(Swapping)이란 주기억장치에 적재한 하나의 프로세스를 보조기억장치에 잠시 적재했다가 필요할 때 다시 꺼내서 사용하는 메모리를 교체하는 기법
스와핑은 현재 잘 사용하지 않음 프로세스 단위로 스와핑을 하면 비효육적이기때문(외부 단편화)
하지만 여기서 가상 메모리가 등장한다.
가상 메모리
가상 메모리는 메인 메모리의 크기는 한정되어 있으므로 물리적인 메모리 크기보다 크기가 큰 프로세스를 실행시킬 수가 없다.
그렇다면 메인 메모리보다 크기가 큰 프로세스를 실행시키고 싶으면 어떻게 해야 할까?
비효율적으로 메인 메모리를 교체해야 할까? 아니. 여기서 나온 방법이 바로 가상 메모리이다.
가상 메모리는 프로세스에게 물리 메모리보다 더 큰 주소 공간을 제공하기 위한 운영체제의 기술이다.
실제 메모리에 전부 올리지 않고, 일부만 메모리에 올린 채로도 프로그램을 실행할 수 있게 한다.
디멘드 페이징
디멘드 페이징(Demand Paging)은 이름 그대로 필요한 부분의 페이지 영역만 물리적 메모리에 할당하는 방법.
디멘드 페이징에서 스와핑의 개념은 프로세스 단위로 뺐다가 재배치하는 과정이 아닌 페이지 단위로 뺐다가 재배치하는 과정.
위와 같은 스와핑의 개념을 가지고 디멘드 페이징에서는 자주 사용하는 페이지만 물리적 메모리에 적재해서 사용하게 된다.
위 그림을 보면 왼쪽 프로세스가 가지는 여러개의 페이지 중 실제 물리 메모리에 올라가는 페이지는 A / C / F 밖에 없다
프로세스가 실행되면 MMU는 실시간으로 페이지 테이블을 확인하고 필요한 메모리가 물리적 메모리에 적재되어 있는지를 확인한다
이때 페이지 테이블의 이때 페이지 테이블의 valid-invalid bit가 v인지 i인지의 차이가 물리 메모리에 해당 페이지가 저장되어 있는지를 알려주는 것
페이지 테이블 엔트리에는 해당 페이지가 Swap-out 될 때 Backing Strorage(보조 기억장치)의 어느 위치에 저장되어 있는지에 대한 정보도 저장하고 있.
만약 valid-invalid bit가 i이면 지금 사용하려는 페이지가 물리적 메모리에 현재 적재되어 있지 않다는 의미인데, 이런 상황을 Page fault라 한다
=> Memory Management Unit 논리 주소를 물리 주소로 변환해주며 메모리 보호나 캐시 관리 등 CPU가 메모리에 접근하는 것을 총 관리해주는 하드웨어
=> valid-invalid bit 페이지 테이블은 각 가상 페이지가 어디에 매핑되는지(혹은 존재하는지)를 저장한 테이블이다.
그 테이블의 각 항목에는 valid-invalid bit (유효-무효 비트)가 붙어 있다. v - 존재 / i - 존재 x
Page fault handling
Page fault는 왜 발생할까?
이유는 모든 페이지가 램(RAM)에 존재하는 게 아니기 때문입니다. 그렇다면 이런 상황을 어떻게 제어해야 할까?
1. 먼저 물리 메모리에 있는 실행될 프로세스는 자신이 사용하고자 하는 페이지가 페이지 테이블에 있는지 확인.
2. 만약 물리 메모리에서 사용하려는 페이지가 없으면 Page fault exception을 발생시키고 커널 모드로 진입.
3. OS는 페이지 테이블 엔트리 정보 속에서 Backing storage 어느 위치에 페이지가 있는지 확인.
4. 확인한 없었던 페이지를 물리 메모리에 로드 .
5. 페이지 테이블을 업데이트.
6. 이제 원래 실행하려고 했던 작업을 실행.
이거 너무 비효율적인거 같은데..
맞는말이다. Backing storage에서 페이지를 찾아서 가져오는 과정은 시간이 꽤 걸리게 된다.
결과적으로 CPU의 효율성을 낮추게 되는것.
그렇다고 물리 메모리를 키우는 건 정말 비효율적인 방법. 때문에 Page fault를 처리하는 과정은 필연적일 수밖에 없다.
그럼 Page fault가 발생할 때마다 이렇게 비효율적인 과정을 거쳐야만 하는 걸까?
여기에서 위에 설명한 Locality라는 개념이 등장한다. 실제로 우리가 사용하는 코드를 생각해보면 사용하는 코드만 재사용해서 실행이 된다. 이를 지역성(Locality)라고 하는데, 지역성 덕분에 한 번 사용하려는 페이지를 페이지 테이블에 넣어놓으면 계속 사용하다가 코드 구역이 넘어가면 Swap-out을 하면 된다. 때문에 결과적으로 보면 Page fault는 지역성 덕분에 우리가 예상하는 만큼 많이 일어나지는 않는다. 그럼에도 불구하고 어느 정도의 Page fault 발생은 감수해야 하는 것.
스와핑(Swapping)의 문제
위에서 설명했던 스와핑을 가상 메모리 관리에도 사용한다. 스와핑은 가장 사용하지 않는 페이지를 Swap-out 해서 Backing storage에 넣어 놓고 다시 필요할 때 Swap-in을 실시. 그렇다면 여기서 어떤 페이지를 선택해서 우선적으로 Swap-out 해야 할까? 여기서 바로 페이지 교체 정책, 페이지 교체 알고리즘이 등장한다. 기준을 제시해주는 것. 페이지 교체 정책에 대해서는 다음 시간에 자세히 살펴보겠습니다.
'CS > 운영체제' 카테고리의 다른 글
7. 세그먼테이션 (3) | 2025.07.25 |
---|---|
4. 동기화 및 병행성 제어 (4) | 2025.07.16 |
3. CPU 스케줄링 (0) | 2025.07.16 |
2. 프로세스와 스레드 (2) | 2025.07.14 |
1. 운영체제 기본 개념 (2) | 2025.07.12 |