java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400
分类: C/C++
2013-03-26 17:05:15
在进程fork子进程的过程中,可以想到的内核中的大致过程就是fork出的子进程,根据load选择出一个的rq,然后把task的状态置为running,把子进程插入到这个rq中,同时更新vruntime,最后根据vruntime确定是否要check preempt(标记TIF_NEED_RESCHED),尝试调度这个新fork出的进程。
在fork进程的copy_process函数中会调用sched_fork函数,会设置task的state设置为TASK_RUNNING,调用cfs的task_fork_fair函数,这个函数的做的事情:
sysctl_sched_latency就是一个sched_entity的调度的最大延时,在时钟中断函数tick中,会判断这轮运行的时间(计算这个依赖于sum_exec_runtime和prev_sum_exec_runtime之差),如果sched_entity运行超过了时间段,那么一定会标记resched,这个细节后面blog会讲到。
代码:
这里为何 se->vruntime要减去 cfs_rq->min_vruntime,查了一下,写到
To prevent boost or penalty in the new cfs_rq caused by delta min_vruntime between the two cfs_rqs, we skip vruntime adjustment.
可以看到内核中的注释说为了避免sched_entity在换cfs_rq的时候,导致了继承了原来cfs_rq的min_vruntime,因为不同的cfs_rq,min_vruntime不相同,可以让在计算vruntime的时候先减去当前cfs_rq的vruntime,在enqueue到其他cfs_rq的时候在加上那个cfs_rq的min_vruntime。
可以看到这个减去cfs_rq的min_vruntime的时机:
在enqueue_entity的时候需要把当前sched_entity的vruntime加上当前cfs_rq的min_vruntime,在do_fork的最后会把当前fork出的子线程放到load最低的cfs_rq中,然后check preempt。
主要逻辑:
active_task的逻辑就是把task enqueue进相关的运行队列
check_preempt_curr函数主要是看当前执行的进程是否可以被抢占(标记TIF_NEED_RESCHED),来运行新的子进程,check_preempt_curr函数判断是否可以resched,主要逻辑在wakeup_preempt_entity函数中。
代码中的注释很好的说明了wakeup_gran函数的作用
这里利用到cfs中的一个调度参数sysctl_sched_wakeup_granularity,这个参数表示wakeup时最小粒度,决定当前sched_entity是否被抢占的时候,如果有sched_entity的vruntime比curr的vruntime大至少sysctl_sched_wakeup_granularity,那么这个时候需要标记重新调度(TIF_NEED_RESCHED),如果差距不足sysctl_sched_wakeup_granularity就不需要抢占,如果没有这个参数,只比较vruntime,会导致抢占的太频繁,这样系统的进程切换也会很频繁,性能也不会很高。如果不抢占会导致,有的sched_entity被调度到需要一定的延时,所以定一个临界值(sysctl_sched_wakeup_granularity)是很有必要的。