概述
linux进程调度的实现一共由四部分组成
(1)、时间记账(就是记录进程已经运行了多长时间了,还要运行多长时间)
(2)、进程选择(加入红黑树)
(3)、调度器入口
(4)、睡眠和唤醒
进程记账:
这个说的挺形象的,就是记录一个进程占用处理器资源的时间长短。既然要记录,那么就需要存放在一个位置,内核说:放在sched_entity结构中吧!
sched_entity结构(2.6内核源码kernel/sched/fair.c 1134 行):
struct sched_entity {
struct load_weight load; /* for load-balancing */
struct rb_node run_node;
struct list_head group_node;
unsigned int on_rq;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime;
u64 prev_sum_exec_runtime;
u64 nr_migrations;
#ifdef CONFIG_SCHEDSTATS
struct sched_statistics statistics;
#endif
#ifdef CONFIG_FAIR_GROUP_SCHED
struct sched_entity *parent;
/* rq on which this entity is (to be) queued: */
struct cfs_rq *cfs_rq;
/* rq "owned" by this entity/group: *
struct cfs_rq *my_q;
#endif
};
注意下上面标准为红色的成员变量。vruntime,这个可以看成是virtual run time 直接翻译过来是虚拟运行时间,书上给的定义叫虚拟实时。
这个vruntime变量就是用来记录进程已经运行的时间长短,或者说 占用处理器已经多长时间了。那么这个时间越长就证明进程运行的时间越长,就越容易被其他的进程挤掉,也就是,其他进程容易抢占进来!linux系统对于进程的调度是要看这个变量来采取行动的。那么这个虚拟实时(就是个时间值)怎么计算出来的呢?(其实这个计算过程就是 进程记账的过程),内核使用update_curr()函数来实现这个过程,源码如下(kernel/sched/fair.c 684 行):
static void update_curr(struct cfs_rq *cfs_rq)
{
struct sched_entity *curr = cfs_rq->curr;
u64 now = rq_of(cfs_rq)->clock_task;
unsigned long delta_exec;
if (unlikely(!curr))
return;
/*
* Get the amount of time the current task was running
* since the last time we changed load (this cannot
* overflow on 32 bits):
*/
delta_exec = (unsigned long)(now - curr->exec_start);
if (!delta_exec)
return;
__update_curr(cfs_rq, curr, delta_exec);
curr->exec_start = now;
if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr);
trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
cpuacct_charge(curtask, delta_exec);
account_group_exec_runtime(curtask, delta_exec);
}
account_cfs_rq_runtime(cfs_rq, delta_exec);
}
说实话,我基本上没看明白。
但是其中的几个变量和函数名 还是很清楚的,curr是一个sched_entity结构指针,接受函数传进的参数。函数参数那个结构是什么?不清楚,反正是把其中的一个成员复制给curr变量。看名字可以知道是指进程当前的状态(当然,包括虚拟实时)。接着定义了一个无符号长整型变量delta_exec,查一下delta有增量的意思,而exec是执行,那么从名字上看就是值执行的增量(这里,其实就是时间的增量)。然后,看到delta_exec被赋值为now和exec_start的差值,exec_start这个可以知道这是开始,现在减开始的,那就是已经运行的时间。最后执行__update_curr函数将当前的进程账本,和已经运行的时间作为参数传入,那么先来看看__update_curr这个函数(kernel/sched/fair.c 664 行):
static inline void
__update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
unsigned long delta_exec)
{
unsigned long delta_exec_weighted;
schedstat_set(curr->statistics.exec_max,
max((u64)delta_exec, curr->statistics.exec_max));
curr->sum_exec_runtime += delta_exec;
schedstat_add(cfs_rq, exec_clock, delta_exec);
delta_exec_weighted = calc_delta_fair(delta_exec, curr);
curr->vruntime += delta_exec_weighted;
update_min_vruntime(cfs_rq);
#if defined CONFIG_SMP && defined CONFIG_FAIR_GROUP_SCHED
cfs_rq->load_unacc_exec_time += delta_exec;
#endif
}
主要还是看红色的代码,传入的参数curr,delta_exec,执行了calc_delta_fair,也是从名字入手,这是计算,计算什么呢?增量,还是公平的,那就是加入了权重,这样计算出了权重值。接着在vruntime上加以权重,这就是进程运行的时间,记账完成。
最后
以上就是矮小纸飞机为你收集整理的linux进程调度的实现——进程记账的全部内容,希望文章能够帮你解决linux进程调度的实现——进程记账所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复