进程调度系列ULK--scheduler_tick()函数

参考的是ULK第三版,Linux2.6.11.12内核版本。

调度程序依靠几个函数来完成调度工作,其中最重要的第一个函数是scheduler_tick函数。

/**
 * 维持当前最新的time_slice计数器
 * 每次时钟节拍到来时,scheduler_tick函数将被调用,以执行与调度相关的操作。
 */
void scheduler_tick(void)
{
	int cpu = smp_processor_id();
	runqueue_t *rq = this_rq();//宏this_rq()产生本地CPU运行队列的地址
	task_t *p = current;

	/**
	 * 把转换为纳秒的TSC的当前值存入本地运行队列的timestamp_last_tick中。这个时间戳由sched_clock获得。
	 */
	rq->timestamp_last_tick = sched_clock();

	/**
	 * 检查当前进程是否是idle进程。swapper进程,就是0号进程
	 */
	if (p == rq->idle) {
		/**
		 * 检查运行队列中除了IDLE进程外,是否还有其他可运行进程。
		 * 如果有,就设置当前进程的TIF_NEED_SCHEDULED字段,以强迫进行调度。
		 */
		if (wake_priority_sleeper(rq))
			goto out;
		rebalance_tick(cpu, rq, SCHED_IDLE);
		/**
		 * 没有必要更新IDLE进程的时间片计数器,所以此处直接返回。
		 */
		return;
	}

	/* Task might have expired already, but not scheduled off yet */
	/**
	 * 检查current->array是否指向本地运行队列的活动链表。
	 * 如果不是,说明进程已经过期但还没有被替换,设置TIF_NEED_SCHEDULED标志,以强制进行重新调度。
	 */
	if (p->array != rq->active) {
		set_tsk_need_resched(p);
		goto out;
	}
	/**
	 * 获得运行队列的自旋锁。
	 */
	spin_lock(&rq->lock);
	/*
	 * The task was running during this tick - update the
	 * time slice counter. Note: we do not update a thread‘s
	 * priority until it either goes to sleep or uses up its
	 * timeslice. This makes it possible for interactive tasks
	 * to use up their timeslices at their highest priority levels.
	 */
	/**
	 * 递减当前进程的时间片计数器,并检查是否已经用完时间片。
	 * 由于进程的调度类型不同,函数所执行的操作也有很大差别。
	 */
	if (rt_task(p)) {/* 如果是实时进程,就进一步根据是FIFO还是RR类型的实时进程 */
		/*
		 * RR tasks need a special form of timeslice management.
		 * FIFO tasks have no timeslices.
		 */
		/**
		 * 对SCHED_RR类型的实时进程,需要递减它的时间片。
		 * 对SCHED_FIFO类型的实时进程,什么都不做,退出。
		 */
		if ((p->policy == SCHED_RR) && !--p->time_slice) {
			/**
			 * 对SCHED_RR类型的实时进程,如果它的时间片已经用完,就执行此下动作,以达到抢占当前进程的目的。
			 * 如果必要的话,就尽快抢占。
			 */
			p->time_slice = task_timeslice(p);/* 重新计算它的时间片,它根据进程的静态优先级来计算它的时间片。 */
			/**
			 * 直到这里,说明进程一定不是第一次运行了,它已经用完了一次它的时间片,将first_time_slice置为0.
			 * 这样,它即使退出,也不会将剩余的时间片还给父进程了。
			 */
			p->first_time_slice = 0;
			/**
			 * 设置调度标志,以达到尽快抢占的目的。
			 */
			set_tsk_need_resched(p);

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

	/**
	 * 运行到此,说明进程是普通进程。现在开始更新普通进程的时间片。
	 */
	if (!--p->time_slice) {/* 首先递减普通进程的时间片计数器。如果用完,继续执行以下操作 */
		/**
		 * 既然用完了,就将当前进程从活动集合中摘除。
		 */
		dequeue_task(p, rq->active);
		/**
		 * 当然,当前进程既然已经过期,就必须设置重新调度标志,以便在中断返回前调用schedule选择另外一个进程来运行。
		 */
		set_tsk_need_resched(p);
		/**
		 * 更新当前进程的动态优先级。
		 * effective_prio根据当前进程的static_prio和sleep_avg字段,计算进程的动态优先级。
		 */
		p->prio = effective_prio(p);
		/**
		 * 重填进程的时间片
		 */
		p->time_slice = task_timeslice(p);
		/**
		 * 既然当前进程的一个时间片已经用完,当然就需要清除first_time_slice标志了。
		 */
		p->first_time_slice = 0;

		/**
		 * 如果本地运行队列的expired_timestamp为0,表示过期进程集合为空。
		 * 并且当前进程马上就会变成过期进程,那么将当前jiffies赋给expired_timestamp
		 * expired_timestamp表示当前队列中,过期队列中最老进程被插入过期队列的时间。
		 */
		if (!rq->expired_timestamp)
			rq->expired_timestamp = jiffies;
		/**
		 * 把当前进程插入过期集合或者活动集合。
		 * TASK_INTERACTIVE判断当前进程是否是一个交互式进程。
		 * TASK_INTERACTIVE宏检查运行队列中的第一个过期进程的等待时间是否已经超过1000个时钟节拍乘以运行队列中的可运行进程数+1,如果是返回1.
		 *                     如果当前进程的静态优先级大于过期进程的静态优先级,也返回1.
		 */
		if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) {
			/**
			 * 当前进程不是交互式进程,或者过期队列中有优先级更高的进程,那么将当前进程插入到过期队列。
			 */
			enqueue_task(p, rq->expired);
			/**
			 * 如果当前进程是过期队列中优先级最高的低,就更新过期队列的最高优先级。
			 */
			if (p->static_prio < rq->best_expired_prio)
				rq->best_expired_prio = p->static_prio;
		} else
			enqueue_task(p, rq->active);/* 进程是交互式进程,并且比过期队列中所有进程的静态优先级高,那么就将它加到活动队列中。这实际上是对交互式进程的优待。 */
	} else {/* 普通进程的时间片还没有用完,需要进一步检查是否时间片太长 */
		/*
		 * Prevent a too long timeslice allowing a task to monopolize
		 * the CPU. We do this by splitting up the timeslice into
		 * smaller pieces.
		 *
		 * Note: this does not mean the task‘s timeslices expire or
		 * get lost in any way, they just might be preempted by
		 * another task of equal priority. (one with higher
		 * priority would have preempted this task already.) We
		 * requeue this task to the end of the list on this priority
		 * level, which is in essence a round-robin of tasks with
		 * equal priority.
		 *
		 * This only applies to tasks in the interactive
		 * delta range with at least TIMESLICE_GRANULARITY to requeue.
		 */
		/**
		 * 检查当前进程的时间片是否太长,因为对于交互式进程来说,它时间片用完后,可能会再插入到活动队列,可能导致这种进程的时间片特别长。
		 */
		if (TASK_INTERACTIVE(p) && !((task_timeslice(p) -
			p->time_slice) % TIMESLICE_GRANULARITY(p)) &&
			(p->time_slice >= TIMESLICE_GRANULARITY(p)) &&
			(p->array == rq->active)) {

			requeue_task(p, rq->active);
			set_tsk_need_resched(p);
		}
	}
out_unlock:
	/**
	 * 释放自旋锁。
	 */
	spin_unlock(&rq->lock);
out:
	/**
	 * 调用rebalance_tick函数,该函数应该保证不同CPU的运行队列包含数量基本相同的可运行进程。
	 */
	rebalance_tick(cpu, rq, NOT_IDLE);
}
时间: 2024-08-03 08:19:07

进程调度系列ULK--scheduler_tick()函数的相关文章

lodash用法系列(3),使用函数

Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>安装:npm install lodash 首先通过npm安装lodash:npm i --save lodash 在js文件中引用lodash:var _ =

C语言K&R习题系列——使用缓冲区函数接受长字符输入

原题: Write a program to print all input lines that are longer than 80 characters.  ,实现起来不算难,关键是用到了缓冲区,很不错的一种思想! /* Write a program to print all input lines  * that are longer than 80 characters  */    #include < stdio.h >    #define MINLENGTH 81    /

php从入门到放弃系列-03.php函数和面向对象

php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: 1 function functionName() 2 { 3 要执行的代码; 4 } 函数命名的准则: 函数的名称应该提示出它的功能 函数名称以字母或下划线开头(不能以数字开头) 二.面向对象 1.类基础语法: 1 <?php 2 class Site { 3 /* 成员变量 */ 4 var $url; 5 var $title; 6 7

代码收藏系列--javascript--日期函数

/** * 将 Date 转化为指定格式的String * 月(M).日(d).12小时(h).24小时(H).分(m).秒(s).周(E).季度(q) 可以用 1-2 个占位符 * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) * @param Date date * @param string fmt * @returns string */ function formatDate(date, fmt) { //author: meizz var

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

RT /** * 递减当前进程的时间片计数器,并检查是否已经用完时间片. * 由于进程的调度类型不同,函数所执行的操作也有很大差别. */ /* 如果是实时进程,就进一步根据是FIFO还是RR类型的实时进程 */ if (rt_task(p)) { /** * 对SCHED_RR类型(时间片轮转)的实时进程,需要递减它的时间片. * 对SCHED_FIFO类型(先进先出)的实时进程,什么都不做, * 退出.在这种情况下, * current进程不可能被比其优先级低或其优先级相等的进程所抢占, *

TypeScript系列6-手册-函数

函数 函数是JavaScript中任意应用程序的基本构件块.可以在函数基础上建立抽象层,模拟类,信息隐藏,模块.在TypeScript中,虽然已经有类和模块,但函数函数仍然扮演着如何做事的关键角色.TypeScript还在标准JavaScript 函数基础上增加了一些新的能力,来使得函数更容易使用. 函数 TypeScript与JavaScript一样,都可以创建命名函数,也可以创建匿名函数.这样允许开发人员根据应用程序选择最合适的方式,不论是在一个API中建立一列函数还是建立一个one-off

JavaScript 闭包系列二 --- 匿名函数及函数的闭包

一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) {    return 2*x;} 2)Function构造函数,把参数列表和函数体都作为字符串,不方便,不建议使用 var double = new Function('x', 'return 2*x;'); 3)函数表达式方式 var double = function(x) {    return 2*x;} 该形式中,等号右边是一个匿名函数,创建函数完毕后,将该函数赋给了变量doubl

C++ STL算法系列1---unique , unique_copy函数

 一.unique函数 类属性算法unique的作用是从输入序列中“删除”所有相邻的重复元素. 该算法删除相邻的重复元素,然后重新排列输入范围内的元素,并且返回一个迭代器(容器的长度没变,只是元素顺序改变了),表示无重复的值范围得结束. 1 // sort words alphabetically so we can find the duplicates 2 sort(words.begin(), words.end()); 3 /* eliminate duplicate words: 4

JavaScript系列:高级函数篇

前言: 本篇主要是介绍 JavaScript使用函数的高级方法,函数是JavaSCript中最有趣的部分,利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:函数回调,高阶函数以及函数柯里化. 1.   函数回调 对于异步编程JavaScript API如SetTimeout或者JQuery的$.ajax又或者越来越火的Node.js编程,在事件循环上都是非阻塞的,意思就是,当你使用异步非阻塞API的使用,调用在回调函数执行之前立即返回,而这些回调在不久之后执行. 1 setT