@@ -78,6 +78,38 @@ recent cpu가 각 프로세스가 "최근에" 받은 CPU 타임을 측정하기
### `load_avg` 계산
마지막으로, 시스템 로드 평균이라고 알려진 load avg는, 지난 1분 동안 실행할 준비가 된 스레드의 평균 수를 추정합니다. `recent_cpu`와 마찬가지로 지수 가중 이동 평균입니다. `priority` 및 `recent_cpu`와 달리 `load_avg`는 스레드별이 아니라 시스템 전체에 적용됩니다. 시스템 부팅 시 `load_avg`는 0으로 초기화됩니다. 그 후 1초에 한 번씩 다음 공식에 따라 업데이트됩니다.
여기서 ready threads는 업데이트 시점에 실행 중이거나 실행할 준비가 된 스레드 수입니다(유휴(idle) 스레드는 포함하지 않음).
일부 테스트에서 가정한 사항에 따라 `load_avg`는 시스템 틱 카운터가 초의 배수에 도달할 때, 즉 `timer_ticks() % TIMER_FREQ == 0`일 때 정확히 업데이트되어야 하며, 다른 시간에는 업데이트되지 않아야 합니다. `threads/thread.c`에 스켈레톤이 있는 `thread_get_load_avg()`를 구현해야 합니다.
`int thread_get_load_avg (void)`
> 현재 시스템 평균 부하에 100을 곱한 값을 가장 가까운 정수로 반올림하여 반환합니다.
### 고정 소수점 연산(Fixed-Point Real Arithmetic)
위의 수식에서 `priority`, `nice`, `ready_threads`는 정수이지만 `recent_cpu`와 `load_avg`는 실수입니다. 안타깝게도 커널을 복잡하고 느리게 만들기에 Pintos는 커널에서 부동 소수점 산술을 지원하지 않습니다. 실제 커널은 종종 같은 이유로 동일한 제한이 있습니다. 즉, 실수에 대한 계산은 정수를 사용하여 시뮬레이션해야 합니다. 이는 어렵지 않지만 많은 학생들이 이를 수행하는 방법을 모릅니다. 이 섹션에서는 이러한 기본 사항을 설명합니다.
기본적인 아이디어는 정수의 가장 오른쪽 비트를 분수로 나타내 처리하는 것입니다. 예를 들어, 부호 있는 32비트 정수의 가장 아래 14비트를 분수 비트로 지정하여 정수 x가 실수 `x/(2^14)`를 나타낼 수 있습니다. 소수점 앞에 17비트, 뒤에 14비트, 부호 비트가 하나가 있어 이를 17.14 고정 소수점 표현(17.14 Fixed-Point Representation)이라고 합니다. 17.14 형식의 숫자는 최대 `(2^31 − 1)/(2^14) ≈ 131,071.999`의 값을 나타냅니다.
`p.q` 고정 소수점 형식을 사용하고 `f = 2^q`라고 가정하면, 위의 정의에 따라 정수나 실수를 f와 곱하여 `p.q` 형식으로 변환할 수 있습니다. 예를 들어, 17.14 형식에서 위의 `load_avg` 계산에 사용된 분수 `59/60`은 `(59/60)2^14 = 16,110`입니다. 고정 소수점 값을 정수로 다시 변환하려면 `f`로 나눕니다. (C의 일반적인 `/` 연산자는 0으로 반올림합니다. 즉, 양수는 내림하고 음수는 올림합니다. 가장 가까운 값으로 반올림하려면 나누기 전에 양수에 `f / 2`를 더하거나 음수에서 뺍니다.)
고정 소수점 수에 대한 많은 연산은 간단합니다. `x`와 `y`를 고정 소수점 수라고 하고, `n`을 정수라고 합시다. 그러면 x와 y의 합은 `x + y`이고, 그 차는 `x - y`입니다. x와 n의 합은 `x + n * f`이고, 차는 `x - n * f`이며, 곱은 `x * n`이고, 몫은 `x / n`입니다.
두 개의 고정 소수점 값을 곱하는 데는 두 가지 복잡한 문제가 있습니다. 첫째, 결과의 소수점이 왼쪽으로 q비트만큼 너무 멀리 있습니다. `(59/60)(59/60)`은 1보다 약간 작아야 하지만, `16,111 × 16,111 = 259,564,321`은 `2^14 = 16,384`보다 훨씬 큰 것을 생각해보세요. `q`비트를 오른쪽으로 이동하면 `259,564,321/2^14 = 15,842` 또는 약 `0.97`이 나옵니다. 둘째, 곱셈은 답이 표현 가능하더라도 오버플로될 수 있습니다. 예를 들어, 17.14 형식의 `64`는 `64 × 2^14 = 1,048,576`이고, 그 제곱 `64^2 = 4,09`6은 17.14 범위 내에 있지만, `1,048,576^2 = 2^40`으로 최대 부호 32비트 정수 값 `2^31 - 1`보다 큽니다. 이에 대한 간단한 해결책은 곱셈을 64비트 연산으로 하는 것입니다. 그러면 x와 y의 곱은 `((int64_t) x) * y / f`이 됩니다다.
두 개의 고정 소수점 값을 나누면 이와 반대의 문제가 생깁니다. 소수점이 너무 오른쪽으로 치우치는데, 나누기 전에 q비트를 왼쪽으로 이동시켜서 이를 해결 할 수 있습니다. 왼쪽으로 이동하면 상위 q비트를 버리게 되는데, 이는 다시 64비트로 나누어 수정할 수 있습니다. 따라서 x를 y로 나누었을 때의 몫은 `((int64_t) x) * f / y`가 됩니다.
이 섹션에서는 두 가지 이유로 q비트 이동 대신 `f`로 곱셈 또는 나눗셈을 일관되게 사용했습니다. 첫째, 곱셈과 나눗셈은 C 이동 연산자의 놀라운 연산자 우선순위가 없습니다. 둘째, 곱셈과 나눗셈은 음수 피연산자에서 잘 정의되어 있지만 C 이동 연산자는 그렇지 않습니다. 구현 시 이러한 문제에 주의하세요.
다음 표는 C에서 고정 소수점 산술 연산을 구현하는 방법을 요약한 것입니다. 표에서 x와 y는 고정 소수점 숫자이고, n은 정수이며, 고정 소수점 숫자는 p + q = 31인 부호 있는 p.q 형식이고, f는 1 << q입니다.