【內核調度】【attach_task】

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)這個是更新什麼呢?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章