페이지 교체(Page Replacement)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
경성대학교 양희재 교수님 수업 영상을 듣고 정리하였습니다.


페이지 교체(Page Replacement)

메모리가 가득차게되면 페이지를 교체해줘야 한다.

  • Demand Paging: 요구되어지는 페이지만 backing store에서 가져온다.
    • 프로그램 실행 계속에 따라 오구페이지가 늘어나고, 언젠가는 메모리가 가득차게 된다.
  • Memory full > victim page
    • page-out: 메모리가 가득하면 추가로 페이지를 가져오기 위해 어떤 페이지는 backing store로 몰아낸다.
    • page-in: 그 빈 공간으로 페이지를 가져온다.

Victim Page, 어느 페이지를 몰아낼 것인가?

  • I/O 시간 절약을 위해
  • 기왕이면 modify 되지 않은 페이지 를 victim으로 선택: modified bit(= dirty bit)

modified bit인 여러 페이지 중에서도 무엇을 victim으로 할 것인가?

  • Random
  • First-In / First-Out(FIFO)
  • 그외
  • page replacement algorithms

페이지 참조 열(Page reference string)

페이지 번호중에서 바로 이어지는 숫자는 스킵하고 넘어가는 것

  • cpu가 내는 주소: 100 101 102 432 612 103 104 611 612
  • page size = 100바이트라면
  • 페이지 번호 = 1 1 1 4 6 1 1 6 6
  • page reference string = 1 4 6 1 6

가상메모리(Virtual Memory)와 요구페이지(Demand Paging)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
경성대학교 양희재 교수님 수업 영상을 듣고 정리하였습니다.


가상메모리(Virtual Memory)

물리 메모리 크기 한계 극복하기 위해 생겨남

물리 메모리보다 큰 프로세스를 실행? e.g) 100MB 메인 메모리에서 200MB 크기의 프로세스 실행

어떻게 해결할까?

프로세스 이미지를 모두 메모리에 올릴 필요는 없다. 즉, 현재 실행에 필요한 부분만 메모리에 올린다.
오류 처리 제외, 배열 일부 제외, 워드프로세스에서 정렬, 표 기능 제외 -> 동적 적재(dynamic loading)과 비슷한 개념

즉, 일반적으로 프로세스를 메모리에 올린다고하면 code, data, stack을 한번에 다 올려야 한다고 생각하지만, 실제로 그럴필요가 없다.

좀 더 현실적으로 프로세스가 여러개가 있고 이를 메모리에 올리려고 한다면, 각 프로세스를 페이지별로 쪼개고(일정 크기로) 각각 필요한 페이지만을 메모리에 올린다. 크기가 큰 프로세스 전체는 메모리에 다 올릴 수가 없지만, 지금 이 순간에 필요한 애들만(쪼개져 있는 애들 중) 올리면 충분히 메모리에 올릴 수가 있게된다. 이렇게 프로세스를 자르되, 요구되는 페이지만 메모리에 올리는 것을 요구페이지 라고 한다.

요구 페이지(Demand Paging)

프로세스를 페이지 단위로 잘라서 메모리에 올리는데 요구되는 페이지만 메모리에 올리고 필요하지 않은 페이지들은 backing store(하드디스크)에 저장해주는 것
페이지가 요구되어지면 그때 그때 들고 올라온다 > 요구페이지

  • 프로세스 이미지는 backing store에 저장
  • 프로세스는 페이지의 집합
  • 지금 필요한 페이지만 메모리에 올린다(load) -> 요구되는(demand)페이지만 메모리에 올린다

요구페이지를 만들기 위해서는 어떤 하드웨어가 필요할까?

  1. valid bit 추가된 페이지 테이블
  2. backing store(=swap device)

페이지 결함(Page fault) = 페이지 부재

접근하려는 페이지가 메모리에 없다(Invalid)

페이지 결함이 일어났다는 것은 cpu 어떤 주소를 냈는데, 그에 해당하는 페이지 엔트리 내용이 0으로 나타났다는 것
이때 페이지 테이블에서 cpu로 전기신호(인터럽트)를 보내게 되고 이를 감지한 cpu는 os에게 routine을 통해 (page fault routine)을 실행하게 된다.

Steps in handling a page fault
Backing store에서 해당 페이지를 가져온다.
cpu가 어떤 주소를 냈고 해당 주소에 대해 invalid를 보게되면 page table에서 cpu에 interrupt를 걸어
os루틴안에서 해당 페이지를 읽어서 메모리로 가져오고 가져온 프레임 번호를 테이블에 기록하고 해당 비트를 valid로 하면 원하는 페이지를 열 수 있다.

용어: pure demand paging vs prepaging

순수 요구페이징(pure demand paging) : 진짜 필요한 애들만 가져오는 것
프로그램이 처음 시작할 적에 지금 필요한게 아니면 아무것도 들고오지 않기 때문에 처음 시작할 때부터 page fault가 일어난다.

그래서 속도는 느리지만, 메모리가 절약된다.

미리페이징(prepaging) : 지금 필요하지 않아도 미리 몇페이지를 가져오는 것

속도는 빠르지만(page fault가 적게 일어남) 메모리 낭비가 있다.

비교: swapping vs demand paging

  • Swapping: 메모리와 backing store를 움직이는 단위가 프로세스 단위로 움직임
  • Demand Paging: 갖고오는단뒤가 페이지 단위임

유효 접근 시간

cpu가 주소를 낼때 빠르게 읽히는게 있고, 느리게 읽히는 게 있는데 이때 평균적인 속도는 얼마정도일까를 유효접근시간이라고 한다.
메모리의 어떤 영역은 빠르게 읽히고, 느리게 읽힐텐데 이에 대한 평균 시간(확률)

  • Effective Access Time
    • probability of a page fault = page fault rate
    • T = (1-p)T + pT
    • 유효접근시간 = (1-page fault가 일어날 확률) * 메모리 읽는 데 걸리는 시간 + page fault가 일어나면 걸리는 시간

이 확률이 낮을 수록 좋은 건데 현실적으로는 어떨까?

지역성의 원리(Locality of reference): cpu가 참조하는 주소가 지역에 모아져있다

메모리 접근은 시간적, 공간적 지역성을 가진다. 실제 페이지 부재 확률은 매우 낮다.

  • 시간적 지역성: cpu가 읽은곳을 나중에도 읽을 수가 있다. 코드에는 반복문이 많고, 한번 읽은 애를 또 읽을 확률이 높다.
  • 공간적지역성: 지금 1000번지를 읽으면 나중에도 1000번지에 인접한 구역을 읽는다. 주소만 들고오는게 아니라 블락 단위로 가져온다.

다른방법

HDD는 접근 시간이 너무 길다 > swap device로 부적합
SSD 또는 느린 저가 DRAM 사용한다!

세그멘테이션(Segmentation)와 외부단편화 그리고 Paged segmentation

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
경성대학교 양희재 교수님 수업 영상을 듣고 정리하였습니다.


세그멘테이션(Segmentation)

프로세스를 논리적 내용(=세그먼트)으로 잘라서 메모리에 배치하는 것

  • 프로세스는 세그먼트(segment)의 집합: 하나의 프로세스는 최소 3개 이상의 세그먼트로 구성
    • 프로세스는 기본적으로 code + data + stack으로 구성되어있다. 하나의 프로그램이 돌기 위해서는 이 3개로 구성된 뭉치가 있어야한다.
  • 세그먼트의 크기는 일반적으로 같지 않다: 코드길이, 데이터길이, 스택길이가 다르고 각자의 길이 모두가 다르기 때문

세그먼트를 메모리에 할당!

MMU내의 재배치 레지스터 값을 바꿈으로써 CPU는 프로세스가 연속된 메모리 공간에 위치한다고 착각한다.
MMU는 세그먼트 테이블(segment table)이 된다.

주소 변환(Address Translation)

  • 논리주소(Logical address): cpu가 내는 주소는 segment 번호(s) + 변위(d)
  • 주소변환: 논리주소 -> 물리주소(Physical address)
    • 세그먼트 테이블 내용: base + limit
    • 세그먼트 번호(s)는 세그먼트 테이블 인덱스 값
    • s에 해당되는 테이블 내용으로 시작 위치 및 한계값 파악
    • 한계(limit)를 넘어서면 segment violation 예외 상황 처리
    • 물리주소 = base[s] + d

segment table은 base(시작번지)와 limit(한계값)으로 이루어져있고, page table은 1차원 배열로 이루어진 반면 segment table은 2차원으로 이루어져있다.

  • 예제
    • 논리주소 (2,100)은 물리주소 무엇인가? 4400
    • 논리주소 (1,500)은 물리주소 무엇인가? 6800(x) 해당주소 없음(limit를 넘어감)
limit base
1000 1400
400 6300
400 4300
1100 3200
1000 4700

보호와 공유

보호(Protection): 해킹 등 방지
모든 주소는 세그먼트 테이블을 경유하므로, 세그먼트 테이블 엔트리마다 r,w,x 비트를 부여한다.
해당 세그먼트에 대한 접근 제어가 가능하고 이는 페이징보다 우월하다.

그 이유는 프로세스를 자를 때 페이징은 일정크기로 자르는데, 그렇게 되면 각 페이지에는 code, data, stack이 구분없이 자려져 있어 섞일 수 있다.
그러나 세그먼트는 부위별로 자르기에 섞이지가 않다. code끼리, data끼리, stack끼리..

즉, 페이징은 한 페이지 안의 r,w,x를 구분짓기가 어려워진다는 의미

공유(Sharing): 메모리 낭비 방지
같은 프로그램을 쓰는 복수개의 프로세스가 있다면, Code + data + stack에서 code는 공유가 가능하다.
(단, non-self-modifying code = reentrant code = pure code인 경우)
프로세스의 세그먼트 테이블 코드 영역이 같은 곳을 가리키게 하고 이는 페이징보다 우월하다.

그러나 대부분의 os는 페이징을 사용한다.

Segmentation의 단점: 외부단편화(External Fragmentation)

세그먼트 크기는 고정이 아니라 가변적이다. 그렇기 때문에 크기가 다른 각 세그먼트를 메모리에 두려면 동적 메모리를 할당 해야한다.
first, best, worst-fit, compaction등 문제

프로세스를 잘라서 메모리에 올리는 이유는 외부단편화를 없애기 위해서인데, 이를 해결해주지 못한다. -> 치명적인 문제

Segmentation + Paging

세그멘테이션은 보호와 공유면에서 효과적이고 페이징은 외부 단편화 문제를 해결해준다.
따라서 세그먼트를 페이징한다! = Paged segmentation, ex) Intel 80x86

  1. 프로세스를 처음에는 세그먼트로 자른다. (code + data + stack)
  2. 각 세그먼트를 페이지로 자른다.

이 또한 그러나 MMU를 두개를 거치게 됨으로 속도면에서는 조금 떨어진다는 단점이 존재한다. = trade off

연속 메모리 할당(Contiguous Memory Allocation)과 페이징(Paging)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
경성대학교 양희재 교수님 수업 영상을 듣고 정리하였습니다.


다중 프로그래밍 환경에서…

  • 부팅 직후 메모리 상태: O/S + big single hole
  • 프로세스 생성 & 종료 반복 -> scattered holes(쪼개져있는 holes)

  • 메모리의 단편화(Memory fragmentation):hole들이 떨어져있음
    • Hole들이 불연속하게 흩어져 있기 때문에 프로세스 적재 불가
    • 외부 단편화(extend fragmentation)발생: 홀들이 연속되어져있지 않고 떨어져 있음
      -> 흩어져 있는 메모리를 합쳐서 사용하면 새 프로세스를 올릴 수 있는데 연속되어 있지 않아 올릴 수 없는 불편함 존재(낭비)
    • 외부 단편화를 최소화 하려면?

외부 단편화를 최소화 하려면? = 연속 메모리 할당

  • First-fit(최초 적합): 메모리를 순차적으로 찾아 제일먼저 발견되는 곳에 넣는 것
  • Best-fit(최적 적합): 제일 사이즈가 밀접한 곳에 넣는 것
  • Worst-fit(최악 적합): 크기가 제일 안맞는 곳에 넣는 것
예) hole: 100/500/600/300/200 KB 프로세스: 212 417 112 426 KB
  • First-fit(최초 적합): 426은 넣을곳이 없음(외부 단편화)
  • Best-fit(최적 적합): 다 넣을 수 있음(메모리를 제일 잘 사용한 방법)
  • Worst-fit(최악 적합): 여전히 426은 들어가지 못함

할당 방식 성능 비교: 속도 및 메모리 이용률

  • 속도면에서는 first-fit이 좋다 -> 제일 먼저 나오는곳에 넣으면 되니까
  • 이용률: first-fit, best-fit -> 메모리가 얼마나 잘 이용되는가를 비교하자면 둘은 거의 비슷하게 나온다.

그러나 best-fit을 사용했음에도 불구하고 외부 단편화로 인한 메모리 낭비는 존재: 메모리의 1/3수준은 여전히 사용불가 수준

  • Compaction: 메모리에 흩어진 hole들을 하나로 모은다. 최적 알고리즘 없음, 고부담(홀을 옮길지, 프로세스를 옮길지, 그 크기에 따라 굉장히 복잡함)
  • 다른 방법은? Paging

페이징(Paging)

프로세스를 일정크기(=페이지)로 잘라서 메모리에 넣는다. (메모리를 일정한 단위로 잘라서 홀 안에 넣는다.)

  • 프로세스는 페이지(page)의 집합
  • 메모리는 프레임(frame)의 집합
  • page의 사이즈와 frame의 사이즈는 똑같다. (해당 page는 각각의 frame에 들어가야 하니까)

페이지를 프레임에 할당한다

  • MMU내의 Relocation register값을 바꿈으로써 cpu는 프로세스가 연속된 메모리 공간에 위치한다고 생각하게 한다.
  • MMU는 페이지테이블(page table)이 된다.

일반적으로 프로세스를 잘라서 넣으면 프로그램은 실행이 되지 않는다. 이를 실행하기 위해서는 cpu를 속여야 한다.

cpu에 Relocation register를 여러개를 둠으로써 cpu는 연속으로 주소를 보냈다고 생각하지만, mmu를 거칠적에는 새로 연산을 해주게된다(다른 값을 내준다)
cpu는 여전히 메모리에 연속적으로 들어갔다고 생각하지만 실제 메모리에는 막 들어가져있다(다른 곳에 매핑 될 수 있도록 해준다)

사실은 메모리 전체를 처음부터 일정크기로 나눈다. 그리고 프로세스를 올릴 적에도 그냥 연속해서 올리는게 아니라 여러개의 페이지로 쪼개 올린다. 흩어져 잇음에도 불구하고 cpu를 속여 mmu값을 적절하게 넣어 연속적이게 보이도록 해준다. logical address는 연속, physical address는 불연속

이를 통해 메모리의 외부단편화 문제를 해결할 수 있게 된다.

페이징에서의 주소 변환(Address Translation)

  • 논리주소(Logical address): cpu가 내는 주소
    • cpu가 내는 주소는 2진수로 표현(전체 m비트)
    • 하위 n비트는 오프셋(offset)또는 변위(displacement, d)
    • 상위 m-n비트는 페이지 번호(p)

이 전체중에 n을 몇비트로 할것인가는 페이지 사이즈를 얼마로 하는가에 따라 달라진다.
한 페이지 사이즈가 16byte라고 한다면 16byte단위로 자른다는것. 그렇다면 하위 n비트에 주어지는것은 4(2의 4승=16byte)비트이다.

  • 주소변환: 논리주소 -> 물리주소(Physical address)
    • 페이지번호(p)는 페이지 테이블 인덱스 값(p=m-n)
    • p에 해당되는 테이블 내용이 프레임 번호(f)
    • 변위(d)는 변하지 않음

예제1) 논리주소 13번지는 물리주소 몇번지일까?

  • page size = 4bytes
  • page table: 5 6 1 2

예제2) 논리주소 3000번지는 물리주소 몇번지일까? & 물리주소 0x1A53번지는 논리주소 몇번지일까?

  • page size = 1KB
  • page table: 1 2 5 4 8 3 0 6

내부단편화, 페이지 테이블

  • 내부단편화(Internal Fragmentation): 프로세스 크기가 페이지 크기의 배수가 아니라면, 마지막 페이지는 한 프레임을 다 채울 수 없다.
  • 남는공간 ≠ 메모리공간

한 페이지의 사이즈는 4byte이고 프로세스의 크게는 15byte라고 하자. 그러면 4개의 페이지가 필요할 것이다.
즉 3번째 페이지는 전체를 다 채우지못하고 1이 남게될 것이다. 4의 배수가 아니니 한 프레임을 다 채울 수 없다. 이 1byte는 누구도 쓸 수 없는 영역이다.

이 또한 메모리의 낭비 이고 이런 경우를 내부단편화라고 한다.

-> 내부단편화의 크기는 미미하다. 크기의 최대 = 페이지사이즈 - 1byte

페이지 테이블 만들기

  • CPU 레지스터로 만든다면?
    • 장점: 주소변환이 빠르다.
    • 단점: 많은 양이 들어가지 못한다.
  • 메인 메모리로 만든다면?
    • 장점: 많은 양을 넣을 수 있다.
    • 단점: 주소변환이 느리다.

둘다 가능한 방법이지만, 현실적으로 사용하기에는 무리가 많다.

  • TLB(Translation Look-aside Buffer): 별도의 s램 칩으로 만든다.
캐시메모리로 만든다. 메인메모리는 d램으로 만들어서 속도가 느린데, 캐시메모리는 s램으로 만들어서 속도가 빠르다.
s램으로 만드는데 캐시는 메인메모리에 있는애를 빨리 가져와서 접근하도록 하는데, 우리는 지금 주소변환을 위한 목적으로 만든것으로
이를 캐시라 하지않고 페이지테이블 목적으로 하이스피드 s램을 사용한것을 TLB라고 한다. 원리는 캐시메모리와 비슷하다.

cpu와 메모리 그 사이에 있는 TLB!
  • 관점: 테이블 엔트리 개수 vs 변환 속도

  • 연습> TLB사용 시 유효 메모리 접근 시간(Effective Memory Access Times)

    • 메모리에서 어떤 내용을 읽어오는데 걸리는 유효한 시간
    • Tm메모리를 읽는데 걸리는 시간 = 100ns, Tb look-aside Buffer를 읽는 시간=20ns, hit ratio=80%
    • h(Tb+Tm) + (1-h)(Tb+Tm+Tm) = 0.8(20+100) + 0.2(20+100+100) = 140ns

보호와 공유

보호(Protection): 해킹 등 방지
모든 주소는 페이지 테이블을 경유하므로, 페이지 테이블 엔트리마다 r,w,x 비트를 두어 해당 페이지에 대한 접근 제어 가능하다.

비트니까 0또는 1의 값을 가질 수 있을 것이고, 만약 r비트가 0인데 내용을 고치고 싶다고 하자.
그러면 page table에서 cpu에 interrupt가 가고, 그러면 cpu는 하던일을 중단하고 os의 특정 routine으로 가서 잘못된 행위를 하려는 프로세스를 강제로 종료시킨다.

이런 방식으로 해킹을 방지한다.

더 나아가, 어짜피 cpu가 내는 모든 주소는 page table을 경유하기 때문에 r,w,x비트를 두어 해당 페이지에 대한 접근 제어가 가능하다.
os가 설정해둔 권한 바깥의 일을 하려고 한다면, 인터럽트를 강제로 걸도록하여 막을 수 있다.

공유(Sharing): 메모리 낭비 방지
같은 프로그램을 쓰는 복수 개의 프로세스가 있다면 Code + data + stack에서 code는 공유가능
(단, non-self-modifying code = reentrant code(재 진입 가능 코드) = pure code인 경우) 프로세스의 페이지 테이블 코드 영역이 같은 곳을 가리키게 되어있다.

code 영역은 안바뀌고 data 영역만 변경된다. (data는 context switching이 이루어지지만 code는 같은곳을 가리킨다.)

메모리의 낭비를 없애는 방법, 메모리 절약(동적적재, 동적연결, 스와핑)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
경성대학교 양희재 교수님 수업 영상을 듣고 정리하였습니다.


메모리 낭비방지

  • Dynamic Loading
  • Dynamic Linking
  • Swapping

동적적재(Dynamic Loading)

loading: 어플리케이션(만들어진 실행파일)을 메인메모리로 올리는 것(적재하는 것)
프로그램 실행에 반드시 필요한 루틴/데이터만 적재

  • 모든 루틴(routine, 함수, 프로시저)이 다 사용되는 것은 아니다(ex.오류처리)
  • 모든 데이터(data)가 다 사용되는 것은 아니다(ex.배열)
  • 자바: 모든 클래스가 다 사용되는 것은 아니다.
  • 실행 시 필요하면 그때 해당 부분을 메모리에 올린다 cf. <->정적적재(static loading)

동적연결(Dynamic Linking)

여러 프로그램에 공통 사용되는 라이브러리(네트워크 관련된 라이브러리(ftp, 이메일..), printf.o 기계어코드 등..)

  • 공통 라이브러리 루틴(library routine)을 메모리에 중복으로 올리는 것은 낭비
  • 라이브러리 루틴 연결을 실행 시까지 미룬다
  • 오직 하나의 라이브러리 루틴만 메모리에 적재되고 다른 애플리케이션 실행 시 이 루틴과 연결(link)된다. cf. <->정적연결(static linking)
  • 공유 라이브러리(shared library, .so) - linux 또는,
  • 동적 연결 라이브러리(Dynamic Linking Library, dll) - Windows

즉, 원래는 실행파일(exe file)을 만들기 전에 링크가 되는데(=정적연결), 링크를 미룬다. 일단 로드된 다음에(p1, p2가 메모리에 올라온 다음에) 링크를 한다.
p1과 p2 둘다 공통적으로 코드 내에 printf를 가지고 있다고 한다면, p1을 실행하려할 때 하드디스크에서 printf를 메인메모리에 가져온다. printf가 메모리에 올라오면 p1과 p2가 실행될때 그때그때 메인메모리에 있는 printf를 링크를 하여 사용한다.

Swapping

메모리에 적재되어 있으나 현재 사용되지 않고 있는 프로세스 이미지를 하드디스크의 특정부분으로 몰아냄
(컴퓨터 한참 사용하다가 중단하는 경우, 그때에도 내 프로그램은 여전히 메모리에 남아있는데 이 부분도 낭비이다.)

  • 메모리 활용도를 높이기 위해 Backing store(swap device, 하드디스크의 일부) 로 몰아내기
  • swap-out(몰아내기) vs swap-in(가져오기)
  • Relocation register 사용으로 적재 위치는 무관
  • 프로세스 크기가 크면 backing store 입출력에 따른 부담이 크다.