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)这个是更新什么呢?