Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片

时间:2022-12-22 21:16:05

RT

	/**
	 * 递减当前进程的时间片计数器,并检查是否已经用完时间片。
	 * 由于进程的调度类型不同,函数所执行的操作也有很大差别。
	 */
	 /* 如果是实时进程,就进一步根据是FIFO还是RR类型的实时进程 */
	if (rt_task(p)) {
		
		/**
		 * 对SCHED_RR类型(时间片轮转)的实时进程,需要递减它的时间片。
		 * 对SCHED_FIFO类型(先进先出)的实时进程,什么都不做,
		 * 退出。在这种情况下,
		 * current进程不可能被比其优先级低或其优先级相等的进程所抢占,
		 * 因此维持当前进程的最新时间片计数器是没有意义的。
		 */
		if ((p->policy == SCHED_RR) && !--p->time_slice) {
			/**
			 * 对SCHED_RR类型的实时进程,如果它的时间片已经用完,
			 * 就执行此下动作,以达到抢占当前进程的目的。
			 * 如果必要的话,就尽快抢占。
			 */
			 /* 重新计算它的时间片,它根据进程的静态优先级来计算它的时间片。 */
			p->time_slice = task_timeslice(p);
			/**
			 * 该标志被fork例程中的copy_process设置,
			 * 直到这里,说明进程一定不是第一次运行了,
			 * 它已经用完了一次它的时间片,将first_time_slice置为0.
			 * 这样,它即使退出,也不会将剩余的时间片还给父进程了。
			 */
			p->first_time_slice = 0;
			/**
			 * 设置调度标志,以达到尽快抢占的目的。
			 * 该标志强制调用schedule函数,以便current指向的进程能
			 * 被另外一个有相同优先级(或更高优先级)的实时进程(如果有)所取代。
			 */
			set_tsk_need_resched(p);

			/* put it at the end of the queue: */
			/**
			 * 将实时进程放到队列末尾。这样,如此链表中还有其他同优先级
			 * 的RR进程,其他进程就能够得到运行了。
			 */
			requeue_task(p, rq->active);
		}
		goto out_unlock;
	}

1.这里解释几个主要的子函数,首先是task_timeslice,重新计算时间片。首先介绍一下基本时间片的概念。

静态优先级本质上决定了进程的基本时间片,即进程用完了以前的时间片时,系统分配给进程的时间片长度。静态优先级和基本时间片的关系用下列公式确定:

Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片

如你所见,静态优先级越高(其值越小),基本时间片就越长。其结果是,与优先级低的进程相比,通常优先级较高的进程获得更长的CPU时间片。

#define NICE_TO_PRIO(nice)	(MAX_RT_PRIO + (nice) + 20)
#define SCALE_PRIO(x, prio) \
	max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE)

static unsigned int task_timeslice(task_t *p)
{
	if (p->static_prio < NICE_TO_PRIO(0))
		return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);
	else
		return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);
}

2.requeue_task函数把进程描述符移到与当前进程优先级相应的运行队列活动链表的尾部。把current指向的进程放到链表的尾部,可以保证在每个优先级与它相同的可运行实时进程获得CPU时间片以前,它不会再次被选择来执行。

这是基于时间片轮转的调度策略。进程描述符的移动通过两个步骤完成:先调用list_del把进程从运行队列的活动链表中删除,然后调用list_add_tail把进程重新插入到同一个活动链表的尾部。