@@ -10,3 +10,52 @@ Pintos 시작 시 스케줄링 알고리즘 정책을 선택할 수 있도록
# 4.4BSD 스케줄러
일반 스케줄러의 목표는 스레드의 다양한 스케줄링 요구 사항을 균형 있게 조정하는 것입니다. 많은 I/O를 수행하는 스레드는 입력 및 출력 장치를 부지런히히 유지하기 위해 빠른 응답 시간이 필요하지만 CPU 타임이 많이 필요하지 않습니다. 반면, CPU 바운드(compute-bound) 스레드는 작업을 완료하기 위해 많은 CPU 타임을 받아야 하지만 빠른 응답 시간은 필요하지 않습니다. 다른 스레드는 이의 사이인, I/O와 컴퓨팅 기간이 나타나며, 이에 따라 요구 사항이 달라집니다. 잘 설계된 스케줄러는 이러한 모든 요구 사항이 있는 스레드를 동시에 수용할 수 있습니다.
프로젝트 1의 경우 이 부록에 설명된 스케줄러를 구현해야 합니다. 이 스케줄러는 멀티 레벨 피드백 큐 스케줄러의 한 예인 [McKusick]에 설명된 스케줄러와 유사합니다. 이 유형의 스케줄러는 실행 준비가 된 스레드의 여러 큐를 유지 관리하며, 각 큐는 다른 우선순위를 가진 스레드를 보유합니다. 스케줄러는 주어진 시간에 가장 우선순위가 높고 비어 있지 않은 큐에서 스레드를 선택합니다. 가장 높은 우선순위 큐에 여러 스레드가 포함되어 있는 경우에는 "라운드 로빈(round robin)" 순서로 실행됩니다.
스케줄러의 여러 측면은 특정 수의 타이머 틱 후에 데이터가 업데이트되어야 합니다. 모든 경우에, 이러한 업데이트는 일반 커널 스레드가 실행될 기회를 갖기 전에 발생해야 하므로, 모든 커널 스레드가 이전 스케줄러 데이터 값 없이 새로 증가된 `timer_ticks()` 값을 볼 수 없습니다.
4.4BSD 스케줄러에는 우선순위 기부가 포함되지 않습니다.
### Niceness
스레드 우선순위는 아래에 주어진 공식을 사용하여 스케줄러에 의해 동적으로 결정됩니다. 그러나 각 스레드에는 스레드가 다른 스레드에 대해 얼마나 "nice"해야 하는지를 결정하는 정수 nice 값도 있습니다. nice가 0이면 스레드 우선순위에 영향을 미치지 않습니다. 최대 20까지의 양의 nice는 스레드의 우선순위를 낮추고 변화한 우선순위에 따라서 CPU 타임을 일부 포기하게 합니다. 반면에 최소 -20까지의 음의 nice는 다른 스레드의 CPU 타임을 빼앗는 경향이 있습니다.
초기 스레드는 nice 값 0으로 시작합니다. 다른 스레드는 부모 스레드에서 상속받은 nice 값으로 시작합니다. 테스트 프로그램에서 사용하기 위해 아래에 설명된 함수를 구현하세요. `threads/thread.c`에서 이에 대한 스켈레톤 정의를 제공했습니다.
`int thread_get_nice (void);`
> 현재 스레드의 nice값을 반환합니다.
`int thread_set_nice (int nice);`
> 현재 스레드의 nice 값을 새로운 nice로 설정하고 새 값에 따라 스레드의 우선순위를 다시 계산합니다(Calculating Priority 참조). 실행 중인 스레드가 더 이상 가장 높은 우선순위를 갖지 않으면 CPU 타임을 양보합니다.
### 우선순위 계산
우리의 스케줄러는 64개의 우선순위를 가지고 있으며, 따라서 64개의 준비 대기열이 있고, 0(PRI_MIN)에서 63(PRI_MAX)까지 번호가 매겨집니다. 숫자가 낮을수록 우선순위가 낮아지므로 우선순위 0은 가장 낮은 우선순위이고 우선순위 63은 가장 높은 우선순위입니다. 스레드 우선순위는 처음에 스레드 초기화 시 계산됩니다. 또한 모든 스레드에 대해 4번째 clock 틱마다 한 번씩 다시 계산됩니다. 어느 경우든 다음 공식에 따라 결정됩니다:
여기서 recent cpu는 스레드가 최근에 사용한 CPU 시간의 추정치이고(아래 참조) nice는 스레드의 nice 값입니다. 결과는 가장 가까운 정수로 버림해야 합니다(잘라냄). recent cpu의 계수 1/4과 nice의 계수 2는 실제로 잘 작동하지만 더 깊은 의미가 없습니다. 계산된 우선순위는 항상 유효 범위 `PRI_MIN`과 `PRI_MAX` 사이에 속하도록 조정됩니다.
이 공식은 최근에 CPU 타임을 받은 스레드에 스케줄러가 다음에 실행될 때 CPU를 재할당하기 위한 낮은 우선순위를 부여합니다. 이것은 기아(starvation)를 방지하는 데 있어서 중요합니다. 최근에 CPU 타임을 받지 못한 스레드는 recent cpu가 0이 되며, 높은 nice 값을 제외하면 곧 CPU 타임을 받을 수 있도록 해야 합니다.
### recent_cpu 계산
recent cpu가 각 프로세스가 "최근에" 받은 CPU 타임을 측정하기를 바랍니다. 더 나아가, 더 최근의 CPU 시간은 덜 최근 CPU 시간보다 더 가중하여 개선하고자 합니다. 한 가지 방법은 n개의 요소로 구성된 배열을 사용하여 지난 n초 동안 받은 CPU 타임을 추적하는 것입니다. 그러나 이 방법은 스레드당 O(n) 공간과 새로운 가중 평균 계산당 O(n) 시간이 필요합니다.
대신 우리는 지수가중이동평균(exponentially weighted moving average)을 사용하는데, 이는 일반적으로 다음과 같은 형태를 취합니다:
> x(0) = f(0),
> x(t) = ax(t − 1) + (1 − a)f(t),
> a = k/(k + 1),
여기서 x(t)는 정수 시간 t ≥ 0에서의 이동 평균이고, f(t)는 평균화되는 함수이며, k > 0은 감소 속도를 제어합니다. 다음과 같이 몇 단계에 걸쳐 공식을 반복할 수 있습니다.