進程next被喚醒後(喚醒前處於TASK_INTERRUPTIBLE狀態),現在已經被schedule()選中即將投入運行,在得到CPU控制權之前需要重新計算其sleep_avg和prio: if (!rt_task(next) && next->activated > 0) ...{
unsigned long long delta = now - next->timestamp; //(1)
if (unlikely((long long)(now - next->timestamp) < 0)) //(2)
delta = 0;
if (next->activated == 1)
delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128;
array = next->array;
dequeue_task(next, array); //(3)
recalc_task_prio(next, next->timestamp + delta); //(4)
enqueue_task(next, array); //(5)
}
//(1)
next->timestamp now
--------------+ +-------------
running | |
| |
| sleeping |
+------------------------+
//(2) 計算delta
//(3) 進程next退出活躍隊列
//(4) 重新計算平均睡眠時間和動態優先順序
//(5) 根據新計算的動態優先順序將進程next重新插入活躍隊列
更新進程p的sleep_avg和prio
The recalc_task_prio( ) function updates the average sleep time and the dynamic priority of a process. It receives as its parameters a process descriptor pointer p and a timestamp now computed by the sched_clock( ) function.
static void recalc_task_prio(task_t *p, unsigned long long now)
{
1. 計算本次睡眠時間(sleep_time)
|-------------------------------------------------------------|
| /* Caller must always ensure 'now >= p->timestamp' */ |
| unsigned long long __sleep_time = now - p->timestamp; |
| unsigned long sleep_time; |
| |
| if (__sleep_time > NS_MAX_SLEEP_AVG) |
| sleep_time = NS_MAX_SLEEP_AVG; |
| else |
| sleep_time = (unsigned long)__sleep_time; |
|-------------------------------------------------------------|
2. 如果sleep_time不大於0(沒有睡眠),就不用更新進程的平均睡眠時間,直接進入第8步
|----------------------------------|
| if (likely(sleep_time > 0)) { |
|----------------------------------|
3. 如果該進程不是核心線程;喚醒前不處於TASK_UNINTERRUPTIBLE狀態;其連續睡眠時間超過給定的睡眠時間極限;則該進程的sleep_avg = 1000 - 100 = 900ms
Checks whether the process is not a kernel thread, whether it is awakening from the TASK_UNINTERRUPTIBLE state (p->activated field equal to -1), and whether it has been continuously asleep beyond a given sleep time threshold.
|----------------------------------------------------------------|
| if (p->mm && p->activated != -1 && |
| sleep_time > INTERACTIVE_SLEEP(p)) { |
| p->sleep_avg = |
| JIFFIES_TO_NS(MAX_SLEEP_AVG - DEF_TIMESLICE);|
|----------------------------------------------------------------|
} else {
4. 根據當前bonus倍增本次連續睡眠時間sleep_time
執行CURRENT_BONUS宏計算進程"原來的平均睡眠時間"(更新前的平均睡眠時間)所對應的bonus值。如果(10 - bonus)大於 0,函數用這個值與sleep_time相乘(放大本次連續睡眠時間sleep_time)。 因為要把sleep_time加到進程的平均睡眠時間上, 所以當前平均睡眠時間越短(sleep_avg對應的bonus值越小), sleep_time增加的倍數就越多。
|----------------------------------------------------------------|
| sleep_time *= (MAX_BONUS - CURRENT_BONUS(p)) ? : 1; |
|----------------------------------------------------------------|
5. 如果進程p喚醒前處於TASK_UNINTERRUPTIBLE狀態;不是核心線程;
a. 如果更新前的平均睡眠時間已經超過了給定的睡眠時間極限,那麼將沒有必要更新平均睡眠時間了(就算加上本次連續睡眠時間sleep_time也沒有意義),所以將sleep_time置為0,直接進入第6步
b. 如果更新前的平均睡眠時間加上本次連續睡眠時間超過了給定的睡眠時間極限,那麼就把p->sleep_avg欄位置為睡眠極限時間並把sleep_avg設定為 0
|------------------------------------------------------------|
| if (p->activated == -1 && p->mm) { |
| if (p->sleep_avg >= INTERACTIVE_SLEEP(p)) |
| sleep_time = 0; |
| else if (p->sleep_avg + sleep_time >= |
| INTERACTIVE_SLEEP(p)) { |
| p->sleep_avg = INTERACTIVE_SLEEP(p); |
| sleep_time = 0; |
| } |
| } |
|------------------------------------------------------------|
6. 把本次連續睡眠時間(sleep_time)加到進程更新前的平均睡眠時間上(p->sleep_avg)
|-----------------------------------------|
| p->sleep_avg += sleep_time; |
|-----------------------------------------|
7. 檢查更新後的平均睡眠時間(p->sleep_avg)是否超過1000個時鐘節拍(以納秒為單位),如果是,函數就把它減到1000個時鐘節拍(以納秒為單位)
|------------------------------------------------|
| if (p->sleep_avg > NS_MAX_SLEEP_AVG) |
| p->sleep_avg = NS_MAX_SLEEP_AVG; |
|------------------------------------------------|
}
}
8. 根據新的平均睡眠時間sleep_avg更新動態優先順序prio
|---------------------------------|
| p->prio = effective_prio(p); |
|---------------------------------|
}