1、attach_one_task&attach_task
task實際上是attach在rq上的,rq是在cpu上的。一個rq上可能包含多個task
/*
* attach_one_task() -- attaches the task returned from detach_one_task() to
* its new rq.
*/
static void attach_one_task(struct rq *rq, struct task_struct *p)
{
struct rq_flags rf;
rq_lock(rq, &rf);
update_rq_clock(rq);
attach_task(rq, p);
rq_unlock(rq, &rf);
}
rq中只包含了三個struct task_struct的結構體指針,分別是
struct task_struct *curr, *idle, *stop
1、所以task_struct和rq的關係是什麼呢?或許不是直接聯繫在一起,但是是通過別的指針聯繫在一起,這個中間媒介極其有可能是se
2、rq的clock分別在什麼時候進行更新?或者說這裏每個相關結構體的時鐘又是什麼時候進行更新?
*rq的clock第一次更新是在attach_one_task進行更新
/*
* attach_task() -- attach the task detached by detach_task() to its new rq.
*/
/* task_struct::on_rq states: */
#define TASK_ON_RQ_QUEUED 1
Or
#define TASK_ON_RQ_MIGRATING 2
static void attach_task(struct rq *rq, struct task_struct *p)
{
lockdep_assert_held(&rq->lock);
BUG_ON(task_rq(p) != rq);
activate_task(rq, p, ENQUEUE_NOCLOCK);
p->on_rq = TASK_ON_RQ_QUEUED;
// 檢查是否搶佔
check_preempt_curr(rq, p, 0);
}
task_rq(p)實際上是通過cpu_id來找到的。
rq上有一個單獨的這樣的字段來實現這個函數
/* cpu of this runqueue: */
int cpu
1、什麼時候p->on_rq的狀態會變成TASK_ON_RQ_MIGRATING
2、check_preempt_curr傳入0是否代表不進行搶佔的意思
3、爲什麼flags傳入的參數是ENQUEUE_NOCLOCK
3、activate_task
flags==ENQUEUE_NOCLOCK
//flag爲 ENQUEUE_NOCLOCK
void activate_task(struct rq *rq, struct task_struct *p, int flags)
{
if (task_contributes_to_load(p))
rq->nr_uninterruptible--;
enqueue_task(rq, p, flags);
}
這裏需要區分的是task_contributes_to_load宏中的flags並不是傳入的flags,而是傳入的task的flags
#define task_contributes_to_load(task) \
((task->state & TASK_UNINTERRUPTIBLE) != 0 && \
(task->flags & PF_FROZEN) == 0 && \
(task->state & TASK_NOLOAD) == 0)
4、enqueue_task(rq, p, ENQUEUE_NOCLOCK)
這裏先一筆帶過一個不是很重要的函數psi_enqueue
/*
* PSI tracks(追蹤) state that persists(持續) across sleeps, such as iowaits and
* memory stalls(阻塞). As a result, it has to distinguish between sleeps,
* where a task's runnable state changes, and requeues, where a task
* and its state are being moved between CPUs and runqueues.
*/
static inline void psi_enqueue(struct task_struct *p, bool wakeup)
{
int clear = 0, set = TSK_RUNNING;
if (static_branch_likely(&psi_disabled))
return;
if (!wakeup || p->sched_psi_wake_requeue) {
if (p->flags & PF_MEMSTALL)
set |= TSK_MEMSTALL;
if (p->sched_psi_wake_requeue)
p->sched_psi_wake_requeue = 0;
} else {
if (p->in_iowait)
clear |= TSK_IOWAIT;
}
psi_task_change(p, clear, set);
}
enqueue_task函數如下:
static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
{
if (!(flags & ENQUEUE_NOCLOCK))
update_rq_clock(rq);
if (!(flags & ENQUEUE_RESTORE)) {
// sched_info_queued發生的概率是unlikely
sched_info_queued(rq, p);/*更新task p入隊的時間戳*/
psi_enqueue(p, flags & ENQUEUE_WAKEUP);
}
p->sched_class->enqueue_task(rq, p, flags);
}
4、enqueue_task_fair(enqueue_task_fair(rq, p, ENQUEUE_NOCLOCK))
/*
* The enqueue_task method is called before nr_running is
* increased. Here we update the fair scheduling stats and
* then put the task into the rbtree:
*/
1、nr_running何時增加呢?
2、更改了fair scheduling 的哪些stats
enqueue_task_fair
static void
enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
{
struct cfs_rq *cfs_rq;
struct sched_entity *se = &p->se;
int task_new = !(flags & ENQUEUE_WAKEUP);
/* Update rq's walt util before update schedutil */
/*進程p開始運行,並將進程p的運行時間累加到整個rq的cumulative_runnable_avg時間上,作爲rq的負載值*/
walt_inc_cumulative_runnable_avg(rq, p);
/*
* The code below (indirectly) updates schedutil which looks at
* the cfs_rq utilization to select a frequency.
* Let's add the task's estimated utilization to the cfs_rq's
* estimated utilization, before we update schedutil.
*/
util_est_enqueue(&rq->cfs, p);
/*
* The code below (indirectly) updates schedutil which looks at
* the cfs_rq utilization to select a frequency.
* Let's update schedtune here to ensure the boost value of the
* current task is accounted for in the selection of the OPP.
*
* We do it also in the case where we enqueue a throttled(阻塞的) task;
* we could argue that a throttled task should not boost a CPU,
* however:
* a) properly implementing(實現) CPU boosting considering throttled
* tasks will increase a lot the complexity of the solution
* b) it's not easy to quantify(量化) the benefits introduced by
* such a more complex solution.
* Thus, for the time being we go for the simple solution and boost
* also for throttled RQs.
*/
/*主要根據這個task的屬性是否需要update task group的boost參數*/
schedtune_enqueue_task(p, cpu_of(rq));
/*
* If in_iowait is set, the code below may not trigger any cpufreq
* utilization updates, so do it here explicitly(明確地) with the IOWAIT flag
* passed.
*/
/*如果進程p是一個iowait的進程,則進行cpu頻率調整*/
if (p->in_iowait)
cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT);
/* 這裏是一個迭代,我們知道,進程有可能是處於一個進程組中的,所以當這個處於進程組中的進程加入到該進程組的隊列中時,要對此隊列向上迭代 */
//前面我們說到過函數在組策略情況下, 調度實體之間存在父子的層次, for_each_sched_entity會從當前調度實體開始, 然後循環向其父調度實體進行更新, 非組調度情況下則只執行一次
for_each_sched_entity(se) {//
/*新創建的進程on_rq爲0,只有入隊之後,其數值纔會被賦值爲TASK_ON_RQ_QUEUED*/
if (se->on_rq)//已經在就緒隊列裏面了,什麼都不用做
break;
/* 如果不是CONFIG_FAIR_GROUP_SCHED,獲取其所在CPU的rq運行隊列的cfs_rq
運行隊列如果是CONFIG_FAIR_GROUP_SCHED,獲取其所在的cfs_rq運行隊列*/
cfs_rq = cfs_rq_of(se);
enqueue_entity(cfs_rq, se, flags);
/*
* end evaluation on encountering(遇到) a throttled cfs_rq
*
* note: in the case of encountering a throttled cfs_rq we will
* post the final h_nr_running increment below.
*/
/*已經throttle,則退出迭代*/
if (cfs_rq_throttled(cfs_rq))
break;
cfs_rq->h_nr_running++;
walt_inc_cfs_cumulative_runnable_avg(cfs_rq, p);
/*將新創建的進程狀態修改爲ENQUEUE_WAKEUP狀態*/
flags = ENQUEUE_WAKEUP;
}
/* 只有se不處於隊列中或者cfs_rq_throttled(cfs_rq)返回真纔會運行這個循環 */
for_each_sched_entity(se) {
cfs_rq = cfs_rq_of(se);
cfs_rq->h_nr_running++;
walt_inc_cfs_cumulative_runnable_avg(cfs_rq, p);
if (cfs_rq_throttled(cfs_rq))
break;
update_load_avg(se, UPDATE_TG);
update_cfs_shares(se);
}
/*增加rq的nr_running的數值*/
if (!se) {
add_nr_running(rq, 1);
if (!task_new)
update_overutilized_status(rq);
}
hrtick_update(rq);
}
1、滿足什麼條件的時候,執行第二個循環
2、htrick_update(rq)這個是更新什麼呢?