Stanford Lecture1 - Introduction to iOS11, Xcode9 and Swift4

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
Stanford Developing iOS11 Apps with Swift를 듣고 정리하였습니다.


What’s in iOS?

iOS는 네개의 층으로 구성되어 있다. 각 층은 Cocoa Touch, Media, Core Services, Core OS이다.
맨 아래층은 하드웨어에 가깝고, 맨 위층은 사용자에 가깝다.

  • Cocoa Touch: 화면이나 버튼 등 유저 인터페이스(UI)와 관련된 layer
  • Media: 음악, 영상, 애니메이션, 사진 등과 관련된 layer
  • Core Services: Foundation이 포함된 layer로 네트워킹, 데이터베이스, 파일관리와 관련된 layer
  • Core OS: C언어로 짜여진 유닉스 OS layer로 앱 개발자로서 거의 직접 접근하지 않는 layer

Xcode 초기 설정

Main.storyboard

UI. xcode를 통해 UI를 그래픽으로 구현 -> 코드를 작성할 필요없이 그래픽으로 구현
버튼, 텍스트필드, 슬라이드 등을 드래그하여 사용하고 버튼과 슬라이더가 스크린에 실시간으로 나타나 원하는대로 편집하고 실행하면 됨

ViewContrller.swift

Main.storyboard에서 만든 UI와 코드를 연결 하는 곳
UIKit는 버튼과 슬라이더 등이 있는 iOS 프레임워크 -> 코코아터치 층 같은 것과 비슷

import UIKit

class ViewController: UIViewController {
  // UIViewController는 UIKit에 들어있고 SuperClass로 ViewContrller는 UIViewController로부터 상속을 받음으로써 제어하는 모든기능을 상속받음
}

카드만들기 - UIButton 써보기

카드를 구성하기 위해 Button이라는 객체를 사용하게 된다. 이 Button은 View를 상속받고 있기 때문에 배경화면을 설정할 수 있다.

UI에 있는 항목 조작하기

UI에 있는 항목을 조작하기 위해서는 실행에 대해 코드를 연결해야 한다.

action: 버튼을 눌렀을 때 메서드를 호출하라는 의미

이 메서드는 인수(arguments)를 가질수도 있고 가지지 않을 수도 있다.
None 은 인수가 없어도 괜찮은 것이고 이외에는 sender 로 설정. 이 경우에는 UIKit를 보내주는 인수를 의미함으로 인수는 꼭 필요하다.

즉, touchCard가 넘어오면 뒤집을 수 있어야 하는데 이러한 소통을 위해 인수가 존재한다.

Type은 인수의 타입으로 Any가 되어있는데 Button으로 변경해줘야 한다.
버튼이 메소드를 보내주는 것이기 때문으로 만약 UIButton으로 변경해주지 않으면 나머지 코드가 작동하지 않는다.

event는 Touch Up Inside로 경계안에서 터치하고 손을 뗄 때 이 메시지를 보내라는 뜻을 가진다.

IBAction와 _ 의미

13번째 줄이 원으로 바뀌었는데, 원에 마우스를 올려보면 어떤 버튼이 메소드를 부르는지 보여준다.

swift의 모든 인수에는 이름이 있으며 메소드를 부를때 이 이름을 포함해야 한다. 즉, 메소드를 호출하거나 코드를 읽을 때 첫번째 인수가 무엇인지 기억할 필요가 없다. 그리고 이 인수의 이름은 두개로 하나는 호출할 때 사용하는 외부이름 이고 하나는 우리가 코드 구현에 사용할 내부 이름 이다.

이름을 하나만 가지는 것도 가능하고 유효하다. withemoji가 emoji가 된다면 외부, 내부 이름 모두 emoji가 된다.
그리고 만약 인수 앞에 _ 가 있다면 그것은 인수가 없다는 것을 의미한다. (거의 사용하지는 않는다.)

touchCard에는 _ 가 있는데 iOS에서 메시지를 보내는 것으로 이건 Objective-C에서 왔고 여기서는 내부 외부 이름이 없었기 때문에 있다.

@IBAction func touchCard(_ sender: UIButton)

카드뒤집기

카드를 클릭했을 때 카드가 뒤집히는 효과를 주기 위해 함수를 하나 선언한다. 두개의 인자를 받는 함수이며, 개별 인자에 대해 ‘외부이름’과 ‘내부이름’을 설정한다. 실제 Button을 클릭했을때, flipCard라는 함수가 호출되고 해당 함수에 두가지 인자를 전달하게 된다. 첫번째 인자는 고스트 이모지이며, 두번째 인자는 눌러진 버튼이다. 이렇게 flipCard로 전달된 두개의 인자를 이용해 우리가 원하는 결과(카드 뒤집기)를 만들어낸다.

func flipCard(withEmoji emoji:String, on button: UIButton) {}

근본적으로 flipCard는 토글이다. flipCard 메소드에게 버튼을 확인해서 이미 유령이면 텍스트 없이 주황색으로 뒤집게 한다.
아니라면 흰 바탕에 유령이 있게 하면 된다. 최소한의 값은 normal state 로 설정해야한다.

func flipCard(withEmoji emoji:String, on button: UIButton) {
    // print("flipCard(withEmoji: \(emoji))")

    // 버튼의 현재 타이틀이 유령으로 되어있는지를 확인
    if button.currentTitle == emoji {
        button.setTitle("", for: UIControl.State.normal)
        button.backgroundColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
    } else { // 유령이 없다면 유령을 넣어줘야 함
        button.setTitle(emoji, for: UIControl.State.normal)
        button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
    }
}

button.currentTitle에 따라 다른내용을 수행하게 한다.

토글? 하나의 설정값으로부터 다른 값으로 전환하는것으로 오직 두가지 상태밖에 없는 상황에서 스위치를 한번 누르면 한 값이 되고, 다시 한번 누르면 다른 값으로 변하는 것을 의미한다.

몇번 뒤집는지 확인해보기

이와 같은 방식으로 선언을 하게 되면 에러를 만나게 된다.

swift는 모든 인스턴스 변수(속성)은 초기화를 해야한다. 즉, 속성엔 초기값이 항상 있어야 한다.

초기화 방법에는 두가지가 있다.

  1. 이니셜라이저를 이용.
    • 이니셜라이저는 단지 특별한 이름을 가진 메소드일 뿐이다. -> init
    • init은 어떤 인수든 다 가질 수 있고 다른 인수를 가진 여러개의 init을 만드는것도 가능하지만 각 init은 모든 변수를 초기화한다.
  2. 값에 0을 쓰는것
var flipCount = 0

위와 같이 0을 할당함으로써 간단하게 초기화하도록 한다. swift의 경우 타입에 매우 엄격하다. 사용되는 거의 모든 변수는 타입을 가지며, Objective-C와의 호환을 위해 타입이 없는 경우가 종종있을 뿐이다. 변수의 타입을 명시적으로 적을수도 있지만, 특정 값을 할당하게 되면 해당 변수의 타입을 추론하기도 한다.

다른 카드 만들기 - UILabel 써보기

UILabel: 읽기전용 텍스트 필드

outlet: 인스턴스 변수(속성)를 만든다.
이 속성은 UILabel을 가리키고 횟수가 바뀌면 스스로를 업데이트 하라고 말해준다.

@IBOutlet weak var flipCountLabel: UILabel!

!는 일단 굳이 초기화를 하지 않아도 된다는 특징이 있다. 초기화를 하지않아도 에러가 뜨지 않는다.

모든 속성을 원한다면 속성 다음 didSet이라는 코드를 추가할 수 있다. didSet을 사용하게 되면 매번 flipCountLabel의 텍스트 값을 할당하는 코드를 넣어주지 않고도 원하는 결과값을 얻을 수 있게 된다. 프로퍼티의 값이 바뀐 후에 실행되는 프로퍼티 감시다(didSet)이다.

var flipCount = 0 {
     didSet {
         flipCountLabel.text = "Flpis: \(flipCount)"
     }
 }

그러면 이 속성이 설정될때마다 이 안의 코드가 실행된다.
이를 속성감시자 라고 하고 이 코드가 변화를 감시하고 있기 때문에 해당 코드를 꺼내 붙이고 없앨 수도 있다.

즉, filpCount가 바뀔때마다 didSet을 실행하고 레이블을 통해 업데이트를 할 것이다.
UI와 인스턴스 변수의 싱크를 맞추기 위해 속성감시자는 많이 사용된다

여러장의 카드 - 배열

여러장의 카드를 만들기 위해서 버튼들을 배열에 넣고 인덱스를 통해 조작하도록 하면 새롭게 생기게 되는 모든 카드에 대해 반복적으로 함수를 선언할 필요가 없다.

UI와 코드의 연결이기 때문에 드래그로! var를 하나 더 만든다.

connection:Outlet Collection > UI에 잇는 것들의 배열(ULButton의 배열이라는 뜻의 특별한 문법이다)

@IBOutlet var cardButtons: [UIButton]!

optional

여러장의 카드를 만들기 위해 배열을 만들엇다면 이 배열속에 저장된 버튼의 인덱스 값을 접근하기 위해 아래와 같은 코드를 작성한다.

let cardNumber = cardButtons.lastIndex(of: sender)

이때 인덱스에 Option+click을 하게 되면 반환값이 Int가 아닌 Int?임을 알 수 있다.

index의 리턴값은 optional 값이다. 물음표가 그 뜻이고 ?는 optional이라는 뜻이다.
optional과 int는 전혀 다른 타입이고 int와는 아무런 상관이 없다

optional은 값이 있거나 없는 두가지 경우만 존재한다. optional 타입인 변수에 값이 할당되지 않은 상태에서 값이 있다고 변수를 사용하게 되면 충돌이 일어나게 된다.

예상하지 못한 nil을 발견했다는 뜻으로 optional을 푸는 과정에서 발생했다는 의미의 에러이다. 이 optional을 정상적으로 사용하기 위한 방법으로 해당 타입의 뒤에 느낌표(!)를 붙이거나 if let 조건문을 활용하는 방법이 있다.

optional 문법은 정말 간단하게 ? ! 이런식으로 되어있는데, 이는 optional이 굉장히 흔히 쓰이기 때문이다.

충돌을 일으키는 느낌표(!)

let cardNumber = cardButtons.lastIndex(of: sender)!

느낌표를 사용하는 경우 연결되지 않은 애를 클릭하면 충돌이 일어나고 이 충돌이 일어나는 이유는 설정되지 않은 optional을 리턴하기 때문이다. 즉 관련된 값이 없으니 프로그램을 충돌시킨다.

그러나 이렇게 충돌이 일어나지 않는 방법을 쓰고 싶은 경우도 있다.

충돌을 일으키지 않는 if let 조건문

조건적으로 설정된 상태에 있는 지 보고 맞으면 쓰고 아니면 쓰지 않도록 한다.

if let cardNumber = cardButtons.lastIndex(of: sender)

이제 이 optional이 설정된 상태에 있다면 코드가 실행되고 아니라면 실행되지 않는다.충돌은 일어나지 않는다.

cardNumber를 이모티콘 배열에서 찾아보기

var emojiChoices = ["👻","🎃","👻","🎃"]

전체 소스코드

import UIKit

class ViewController: UIViewController {

    var flipCount = 0 {
        didSet {
            flipCountLabel.text = "Flpis: \(flipCount)"
        }
    }

    @IBOutlet var cardButtons: [UIButton]!

    @IBOutlet weak var flipCountLabel: UILabel!

    var emojiChoices = ["👻","🎃","👻","🎃"]

    @IBAction func touchCard(_ sender: UIButton) {
        flipCount += 1
//        flipCountLabel.text = "Flpis: \(flipCount)"
//        flipCard(withEmoji: "👻", on: sender)
        if let cardNumber = cardButtons.lastIndex(of: sender) {
            flipCard(withEmoji: emojiChoices[cardNumber], on: sender)
            print("cardNumber: \(cardNumber)")
        } else {
            print("chosen card was not in cardButtons")
        }
    }


    func flipCard(withEmoji emoji:String, on button: UIButton) {
        // print("flipCard(withEmoji: \(emoji))")
        // 버튼의 현재 타이틀이 유령으로 되어있는지를 확인
        if button.currentTitle == emoji {
            button.setTitle("", for: UIControl.State.normal)
            button.backgroundColor = #colorLiteral(red: 1, green: 0.5763723254, blue: 0, alpha: 1)
        } else { // 유령이 없다면 유령을 넣어줘야 함
            button.setTitle(emoji, for: UIControl.State.normal)
            button.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
        }
    }
}
  • 참고: UI와 코드 모두 건드리는 것의 이름을 바꿀때는 cmd+rename

디스크 스케줄링(Disk Scheduling) + Disk Scheduling Algorithm

|

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


디스크 스케줄링(Disk Scheduling)

  • 디스크 접근시간
    • Seek time + retotatinal delay(track이 도는시간) + transfer time
    • 탐색시간(seek time)이 가장 크다 = header를 움직이는 시간
  • 다중 프로그래밍 환경(지금 우리의 프로그래밍 환경)
    • 디스크 큐(disk queue)에는 많은 요청(request)들이 쌓여있다.
    • 요청들을 어떻게 처리하면 탐색시간을 줄일 수 있을까? = 디스크 스케줄링 알고리즘

디스크 스케줄링 알고리즘

  • FCFS(First Come First Served)
  • Shortest-Seek-Time-First
  • Scan Scheduling
  • Elevator Algorithm

FCFS(First Come First Served)

가장 먼저온 것을 가장 먼저 실행 -> Simple and fair

  • 예제
    • 200cylinder disk, 0 .. 199
    • Disk queue: 98 183 37 122 14 124 65 67
    • Head is currently ay cylinder 53
    • Total head movement = 640 cylinders
    • Is FCFS efficient?

Shortest-Seek-Time-First

Select the request with the minimum seek time from the current head position
-> seek time(disk head가 움직이는 시간)이 최소화 되는 것을 가장 먼저 해라

  • 예제
    • 200cylinder disk, 0 .. 199
    • Disk queue: 98 183 37 122 14 124 65 67
    • Head is currently ay cylinder 53
    • Total head movement = 236 cylinders
  • 문제점
    • Starvation
    • Is SSTF optimal? No!

Scan Scheduling

Scan disk: The head continuously scans back and forth across the disk
disk head가 디스크 전체에 걸쳐서 스캔하는것(들어갔다 나갔다가..)

  • 예제
    • 200cylinder disk, 0 .. 199
    • Disk queue: 98 183 37 122 14 124 65 67
    • Head is currently ay cylinder 53 (moving toward 0)
    • Total head movement = 53 + 183 cylinder(less time)
  • 토론
    • Assume a uniform distribution of requests for cylinder: request들이 고르게 분포되어있다.
    • Circular SCAN is neccessary?
      • 일반적으로 request는 하드디스크에 골고루 분포되어있다.

SCAN 알고리즘의 변종

  • C-SCAN
  • LOOK
  • C-LOOK

C-SCAN

Treats the cylinders as a circular list htat wraps around from the final cylinder to the first one

마지막 부분이 첫번째 부분과 연결되어있는 것처럼 생긴 것. 원형모양처럼!
움직인 거리는 더 많을 수 있어도 시간은 훨씬 단축된다!

LOOK

The head goes only as far as the final request in each direction
Look for a request before continuing to meve in a given direction

0을 거치지 않고 바로 turn! 즉 head는 final request까지만 간다!
해당되는 방향으로 계속 갈 것인가를 결정하기 전에 그 이후의 큐가 있는지를 확인하고 움직인다.

C-LOOK

LOOK version of C-SCAN

request의 끝까지만 가는것. 199까지 가는게 아니라 183까지만 간다!

Elevator Algorithm

SCAN알고리즘을 부르는 또다른 호칭

SCAN and variants

  • SCAN & C-SCAN
  • LOOK & C-LOOK

The head behaves just like an elevator in a building. first servicing all the requests going up, and then reversing to service requests the other way

올리가는 것 쭈욱 서비스하고 내려오면서 또 해결

파일할당(File Allocation)

|

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


  • 컴퓨터 시스템 자원 관리 CPU: 프로세스 관리(CPU 스케줄링, 프로세스 동기화)
    • 주기억장치: 메인 메모리 관리(페이징, 가상 메모리)
    • 보조기억장치: 파일시스템
  • 보조기억장치(하드 디스크)
    • 하드디스크: track(cylinder), sector
    • Sector size = 512 bytes, cf. Block size: sector를 모아둔 것->block
    • 디스크는 블록 단위의 읽기/쓰기(block device)함 <-> character device
    • 디스크 = pool of free blocks, free blocks들의 모음
    • 각각의 파일에 대해 free block을 어떻게 할당해줄까?

파일할당

  • 연속 할당(Contiguous Allocation)
  • 연결 할당(Linked Allocation)
  • 색인 할당(Indexed Allocation)

연속 할당(Contiguous Allocation)

각 파일에 대해 디스크 상의 연속된 블록을 할당(배열)

장점 : 디스크 헤더의 이동 최고화 = 빠른 I/O 성능
단점 : 외부 단편화로 인한 디스크 공간의 낭비 발생

옛날 IBM VM/CMS 에서 사용했으며 동영상, 음악, VOD 등에 적합하다.
파일의 크기가 큰 애들이 흩어져있다면 가져오는데 시간이 오래걸리지만 모여있으면 가져오는데에 시간이 적게걸린다.

  • sequential access(순차접근): 순서적으로 읽기 가능
  • direct access(직접접근): 특정 부분을 바로 읽기 가능
    • directory: 파일들의 정보를 모아둔 테이블

연속 할당의 단점

파일이 삭제되면 hole이 생성되는데, 이렇듯 파일의 생성/삭제가 반복되면 곳곳에 hole들이 흩어져있게 된다. 그렇다면 새로운 파일을 어느 hole에 둘 것인가에 대한 고민이 생기고 이때 외부단편화 문제가 발생하게 된다

  • 디스크의 compaction은 가능하지만 시간이 굉장히 오래 걸린다.

즉, 외부 단편화로 인한 디스크 공간의 낭비 발생한다.

더 나아가, 사실 파일 생성 당시 파일의 크기를 알 수 없다. 즉, 파일을 어느 hole에 넣어야 할 지 알 수가 없다.
더 나아가 파일의 크기가 계속 증가할 수 있다(log file) -> 기존의 hole 배치로는 불가!

연결 할당(Linked Allocation)

파일을 연속적으로 나누는게 아니고, 자료구조의 linked list로 바라봄으로써 아무데나 들어갈 수 있다.

file = linked list of data blocks

파일 디렉토리(directory)는 제일 처음 블록을 가리킨다.
각 블록은 포인터 저장(다음 블록은 어디있는지)을 위한 4바이트 또는 이상을 소모한다.

새로운 파일 만들기

  1. 비어있는 임의의 블록을 첫 블록으로
  2. 파일이 커지면 다른 블록을 할당 받고 연결 -> 장점 :외부 단편화 발생하지 않음

연결 할당의 단점

순서대로 읽기(sequential access)만 가능하며, direct access(중간부터 읽기) 불가하며 포인터 저장 위해 4바이트 이상 손실한다.

  • 낮은 신뢰성: 포인터 끊어지면 이하 접근 불가하다
  • 느린 속도: 헤더의 움직임

연결 할당의 개선점: FAT 파일 시스템

File Allocation Table 파일 시스템으로 MS-DOS, OS/2, Windows등에서 사용한다.

연결할당의 변종으로 포인터들만 모은 테이블(FAT)을 별도 블록에 저장하고 FAT 손실 시 복구를 위해 이중 저장하는 시스템을 갖는다. 이는 direct access도 가능하며(FAT내용을 가져와 헤더를 움직임) FAT는 일반적으로 메모리 캐싱을 한다.

색인 할당(Indexed Allocation)

연결할당과 같이 아무데에나 파일을 할당할 수 있고, 그 정보를 저장하는 블락을 할당

데이터 블럭 외에 각 파일 당 한 개의 인덱스 블록이 존재한다.
인덱스 블록은 포인터의 모음이며 디렉토리는 인덱스 블록을 가리킨다. Unix/Linux 등에서 사용한다.

장점 : direct access 가능, 외부 단편화 없음
단점 : 인덱스 블록 할당에 따른 저장공간 손실
-> 예: 1바이트 파일 위에 데이터 1블록 + 인덱스 1블록

파일의 최대 크기

  • 예제: 1블록 = 512 bytes = 4bytes x 128개의 인덱스 = 128*512bytes = 64KB
  • 예제: 1블록 = 1KB = 4bytes x 256개의 인덱스 = 256*1KB = 256KB
  • 해결방법: Linked, Multilevel index, Combined 등
    • Linked: 인덱스블락이 다른 인덱스 블락을 가리키도록 함
    • Multilevel index: 블락을 여러개를 둠
    • Combined: Linked와 Multilevel index를 합친 것

페이지 크기(Page size)

|

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


요구페이지 혹은 페이징을 할때 프로세스를 일정크기만큼 자른다고 했는데, 자른 하나하나를 페이지라고 한다.
그럼 이럴때 한 페이지의 사이즈는 어떻게 될까? 큰게 좋을까, 작은게 좋을까?

페이지 크기(Page size)

  • 일반적인 크기: 4KB -> 4MB
  • 점차 커지는 경향: 하나의 프로세스 크기들이 커지고 있기 때문

페이지 크기 영향

  • 내부 단편화: 페이지 크기가 작을수록 좋다.
  • Page-in, Page-out 시간: 페이지 크기가 클수록 좋다.
  • 페이지 테이블 크기: 페이지 크기가 클수록 좋다.
  • Memory resolution: 해상도. 페이지 크기가 작을수록 좋다.
  • Page fault 발생확률: 페이지 크기가 클수록 좋다.

메모리도 커지고, 프로세스도 커져가는 상황에서 그에 따라 페이지 사이즈도 점차 커지는 경향이 있다.

기술 동향: 페이지 테이블

원래는 별도의 chip(TLB 캐시로 만들지만, 기술 발달에 따라 캐시 메모리는 on-chip형태로 TLB 역시 on-chip 내장(cpu안에 들어감)

프레임 할당(Allocation of Frames)

|

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


쓰레싱(Fhrashing)

page fault가 너무 자주일어나는 것을 쓰레싱이라고 한다.

  • cpu utilization vs Degree of multiprogramming(cpu이용률 vs 메인메모리에 올라온 프로세스의 수)
    • 프로세스 개수 증가 > cpu 이용률 증가
    • 일정 범위를 넘어서면 cpu 이용률 감소
    • 이유: 빈번한 page in/out
    • Thrashing: i/o 시간 증가 때문
  • 쓰레싱 극복
    • Global replacement 보다는 local replacement
    • 프로세스당 충분한/적절한 수의 메모리(프레임) 할당

프레임 할당(Allocation of Frames)

어떤 프로세스에게 얼마만큼의 프레임을 할당해줄 것인가를 결정

  • 정적할당(static allocation): 프로세스를 실행해보지않고 프로세스 사이즈만 보고 결정
    • 균등할당(Equal allocation): 모든 프로세스에게 동일한 비율로 할당
    • 비례할당(Proportional allocation): 용량이 클수록 비례하게 할당
  • 동적할당(dynamic allocation): 실제 실행을 해보고 결정
    • Working set model
    • Page fault frequency
    • etc

Working set model

  • Locality vs working set
    • Locality: 페이지들이 모여있는 것 (cpu가 내는 주소는 모여져있다.)
    • working set: 과거 일정 시간대에 사용된 페이지
  • Working set window(=Δ): 현 시점을 기준으로 얼마를 과거로 보는지에 대한 시간
  • Working set 크기 만큼의 프레임 할당

Locality만큼 프레임을 할당해주면 좋겠지만, 미래의 Locality를 알수가 없기때문에 과거에 참조했던 페이지,
즉 과거는 얼마만큼 과거나면 working set window seconds 이는 os를 만든사람이 결정한다.

과거 사용된 페이지(working set)만큼을 할당해준다. 그렇게 되면 쓰레싱이 발생하지도 않으면서 메모리를 너무 낭비하지도 않을 것이다.

Page-Fault Frequency(PFF)

  • Page fault 발생 비율의 상한/하한선
  • 상한선 초과 프로세스에 더 많은 프레임 할당
  • 하한선 이하 프로세스의 프레임은 회수