메모리 가상화 (Memory Virtualization)
- OS는 physical memory를 가상화함
- OS는 가상 메모리 공간을 각 프로세스마다 제공함
=> 각 프로세스가 전체 메모리를 사용하는 것처럼 보임
메모리 가상화의 목표
Transparency (투명성)
- 프로세스는 메모리가 공유되고 있다는 사실을 인식하지 못해야함
- 프로그래밍을 위한 편리한 추상화 제공 (크고 연속적인 메모리 공간)
Efficiency (효율성)
- 다양한 크기의 요청으로 인한 fragmentation(단편화)를 최소화함 (공간)
- 하드웨어의 도움을 받음(시간)
역시 자본이 최고야
Protection (보호)
- 다른 프로세스로부터 프로세스들과 OS를 보호
- Isolation : 프로세스가 fail 하더라도 다른 프로세스들에 영향을 주지않음
- Cooperating processes 끼리 서로 메모리 일부를 공유할수있음
OS in The Early System
- 메모리에 오직 하나의 프로세스만 로드할수있음.
- 낮은 활용도( utilization )와 효율성( efficiency )

Multiprogramming and Time Sharing
- 메모리에 여러 프로세스를 로드
=> 하나씩 짧게 실행, 프로세스를 스위칭하여 실행
=> 활용도( utilization )과 효율성( efficiency ) 증대
==> 단점 : 다른 프로세스의 잘못된 메모리 액세스 문제가 발생할수있음

메모리 공간 ( Address Space )
- OS는 물리적 메모리의 추상화를 생성
- 주소 공간에는 실행 중인 프로세스에 대한 모든 것이 포함되어있음
- (프로그램) 코드(code), 힙(heap), 스택(stack) 등으로 구성 (free는 안 쓰고 있는 공간)
코드( Code ) : instruction이 있는 곳
힙( Heap ) : 동적으로 할당된 메모리 영역
-> C언어에서는 malloc, 객체지향언어에서는 new
스택( Stack ) : 주소(return address)나 값(value)을 저장하는 영역
-> 지역 변수( local variables ), arguments to routines

이런식으로 물리 메모리와 주소공간을 연결한다.

가상 주소 ( Virtual Address )
- 모든 프로그램에서의 주소는 모두 가상 주소
=> OS는 가상주소를 물리주소로 바꿔준다
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
printf("location of code : %p\n", (void *) main);
printf("location of heap : %p\n", (void *) malloc(1));
int x = 3;
printf("location of stack : %p\n", (void *) &x);
return x;
}


주소 변환 ( Address Translation )
효율적인 메모리 가상화 및 제어
- 메모리 가상화는 효율성 및 제어를 위해 limited direct execution(LDE) 과 유사한 전략을 사용
- 메모리 가상화에서 효율성과 제어는 하드웨어 서포트로 달성됨
EX) 레지스터, TLB( (Translation Look-aside Buffer ), 페이지 테이블
- 하드웨어는 가상 주소를 실제 주소로 변환
=> 원하는 정보는 실제로는 물리적 주소에 저장됨
- OS는 메모리의 어떤 위치가 비어 있고 어떤 위치가 사용 중인지 추적해야함
가정
- 사용자의 주소 공간-> 물리적 메모리에 연속적으로 배치되어야함
- 메모리 -> 주소 공간의 크기가 물리적 메모리 크기보다 작아야 함
- 각 주소 공간의 크기가 정확히 같아야함
이러한 C언어 코드가 있다고 한다면, (이 코드는 Load, Increment, Store를 하는 코드)
void func()
int x;
...
x = x + 3; // this is the line of code we are interested in
128 : movl 0x0(%ebx), %eax ; load 0+ebx into eax
132 : addl $0x03, %eax ; add 3 to eax register
135 : movl %eax, 0x0(%ebx) ; store eax back to mem
어셈블리어 코드는 이러하다(Load, Add, Store)

=> OS는 프로세스를 물리적 메모리에 저장한다(주소 0이 아님), 하지만 주소공간(가상메모리)은 0에서부터 시작한다

Software-based relocation (정적)
- OS가 각 프로그램을 메모리에 로드하기 전에 다시 작성
- 정적( static ) 데이터 및 함수의 주소 변경

장점
- 하드웨어 서포트가 필요하지않음
단점
- 프로세스가 OS 또는 다른 프로세스의 메모리 영역을 파괴할 수 있음
- 모든 메모리의 주소를 읽을수있음(No privacy)
- 주소 공간을 배치한 후에는 변경할 수 없음
- external fragmentation(외부 단편화)로 인해 새 프로세스를 할당받지 못할 수 있음
Hardware-based relocation (동적)
- MMU( Memory Management Unit , 메모리 관리 유닛)는 모든 메모리 참조 명령어의 주소변환을 수행함
- 하드웨어에 의한 Protection
=> 만약 가상 주소가 유효하지 않으면 MMU는 예외( exception )를 발생시킴
- OS는 현재 프로세스의 유효한 주소 공간에 대한 정보를 MMU에 전달

프로그램이 실행될때, Os는 프로세스가 물리 메모리의 어디에 load될지 결정

아까와 같은 코드에서는 이렇게 변환된다


메모리 가상화에 있어서 OS 이슈
=> 3가지 시점이 중요
- 프로세스가 실행을 시작할때 : 주소 공간을 위한 물리 메모리에서의 공간 찾기
-> free list : 사용하지않는 물리적 메모리 범위의 리스트

- 프로세스가 종료될때 : 메모리 회수( Reclaiming the memory for use ) (= 다시 free list로)

- 프로세스가 컨텍스트 스위칭될 때 : Saving and storing the base-and-bounds pair => PCB( process control block )에

Segmentation
Base and Bound Approach의 비효율성
- 큰 덩어리의 free space
- free space는 물리 메모리 공간을 차지함
- 주소공간이 물리적 메모리에 들어가지 않아, 실행할 수 없을 수도 있음
=> 주소 공간을 logical segments로 나누기
- segments는 특정 길이의 주소 공간의 연속적인 부분
- 각 segment는 주소 공간의 논리적 엔티티 ( logical entity ) 에 해당됨
- 논리적으로 다른 segment : code, stack, heap
각 segment 는 독립적으로
- 물리적 메모리의 다른 부분에 배치될 수 있음
- 확장 또는 축소
- 각 segment 마다 base, bound가 존재

offset(주소공간, 가상 메모리 주소)과 base를 알면 물리 메모리 주소를 알수있음

=> 가상 메모리 주소 + base는 정확한 물리 메모리 주소가 아님

=> 가상 메모리 -> 물리 메모리 변환을 통해 프로그램이 필요로 하는 메모리 주소 : 34920
????????????뭔소리지...
- 만약 잘못된 주소 (7KB 같이) heap이 참조하는 값 범위 밖이면, OS는 segmentation fault를 일으킨다, 하드웨어는 이를 out of bounds 라고함


세그먼트 참조 ( Referring to Segment )

// get top 2 bits of 14-bit VA
Segment = (VirtualAddress & SEG_MASK) >> SEG_SHIFT
// now get offset
Offset = VirtualAddress & OFFSET_MASK
if (Offset >= Bounds[Segment])
RaiseException(PROTECTION_FAULT)
else
PhysAddr = Base[Segment] + Offset
Register = AccessMemory(PhysAddr)

Stack segment 참조
- stack은 위로 올라감
- 하드웨어는 그 방향을 확인함
=> 1: 양의 방향, 0: 음의 방향


세그먼트는 주소 공간 간에서 공유가능
- Code sharing (요즘도 쓰임)
- Protection bits
=> 읽기 read , 쓰기 write , 실행 execute 권한을 나타내는 세그먼트당 몇 개의 비트가 더 필요

Fine-Grained and CoarseGrained
- Coarse-Grained (넓게) : 작은 숫자의 segmentation
=> code, heap, stack
- Fine-Grained segmentation (상세하게) : 보다 유연한 주소공간
=> 더 많은 segment 를 지원하기 위해 하드웨어에서의 segment table이 필요함
단편화 ( Fragmentation )
- 외부 단편화( External Fragmentation ): 물리적 메모리에 작은 구멍이 생겨서 새로운 세그먼트를 할당하기 어려움
=> 물리적 메모리에 연속적으로 할당할수있는 공간이 없음
- Compaction (압축) : 물리적 메모리에서 기존 세그먼트를 재정렬
=>
- 실행 중인 프로세스를 중지
- 데이터를 어딘가로 복사
- 세그먼트 레지스터 값을 변경
=> 뭔가 작업이 많다

Segmentation 의 장점
- 스택과 힙이 독립적으로 확장 가능
- 내부 단편화 없음
- 빠르고 간편하며 하드웨어에 적합함
- 간편한 세그먼트 공유
=> base/limit 쌍에 동일한 변환을 적용, 세그먼트 수준에서 코드/데이터 공유(예: 공유 라이브러리)
- 각 세그먼트의 동적 재배치 지원
Segmentation 의 단점
- 각 세그먼트는 연속적으로 할당되어야 함
- 외부 단편화
- 큰 세그먼트를 위한 물리적 메모리가 충분하지 않을 수 있음
- 하나의 논리적 세그먼트에 크지만 사용 빈도가 낮은 힙이 모두 있는 경우 전체 힙이 여전히 메모리에 상주해야 함
'운영체제' 카테고리의 다른 글
| Chap 6. (1) | 2023.10.23 |
|---|---|
| Chap 5. (0) | 2023.10.22 |
| Chap 3. (0) | 2023.10.15 |
| Chap 2. 프로세스 (0) | 2023.10.11 |
| Chap 1. OS 개요 (2) | 2023.10.10 |