进程调度与进程切换

进程调度

void schedule(void) 进程调度函数

 

void schedule (void)
{
	int i, next, c;
	struct task_struct **p;	// 任务结构指针的指针。

/* 检测alarm(进程的报警定时值),唤醒任何已得到信号的可中断任务 */

// 从任务数组中最后一个任务开始检测alarm。
	for (p = &LAST_TASK; p > &FIRST_TASK; --p)
		if (*p)
		{
// 如果任务的alarm 时间已经过期(alarm<jiffies),则在信号位图中置SIGALRM 信号,然后清alarm。
// jiffies 是系统从开机开始算起的滴答数(10ms/滴答)。定义在sched.h 第139 行。
			if ((*p)->alarm && (*p)->alarm < jiffies)
			{
				(*p)->signal |= (1 << (SIGALRM - 1));
				(*p)->alarm = 0;
			}
// 如果信号位图中除被阻塞的信号外还有其它信号,并且任务处于可中断状态,则置任务为就绪状态。
// 其中'~(_BLOCKABLE & (*p)->blocked)'用于忽略被阻塞的信号,但SIGKILL 和SIGSTOP 不能被阻塞。
			if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
					(*p)->state == TASK_INTERRUPTIBLE)
				(*p)->state = TASK_RUNNING;	//置为就绪(可执行)状态。
		}

  /* 这里是调度程序的主要部分 */

	while (1)
	{
		c = -1;
		next = 0;
		i = NR_TASKS;
		p = &task[NR_TASKS];
// 这段代码也是从任务数组的最后一个任务开始循环处理,并跳过不含任务的数组槽。比较每个就绪
// 状态任务的counter(任务运行时间的递减滴答计数)值,哪一个值大,运行时间还不长,next 就
// 指向哪个的任务号。
		while (--i)
		{
			if (!*--p)
				continue;
			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
				c = (*p)->counter, next = i;
		}
      // 如果比较得出有counter 值大于0 的结果,则退出124 行开始的循环,执行任务切换(141 行)。
		if (c)
			break;
      // 否则就根据每个任务的优先权值,更新每一个任务的counter 值,然后回到125 行重新比较。
      // counter 值的计算方式为counter = counter /2 + priority。[右边counter=0??]
		for (p = &LAST_TASK; p > &FIRST_TASK; --p)
			if (*p)
				(*p)->counter = ((*p)->counter >> 1) + (*p)->priority;
	}
	switch_to (next);		// 切换到任务号为next 的任务,并运行之。
}

看这个函数,它的for循环,可以看到,他是从后往前遍历各个进程,也就是Task数组里的各个task_struct(表示各个进程对象的结构体),如果进程不为空,进程的alarm不为空,并且<jiffies

这个alarm就是一个报警器(定时器),当到了定时的时间,就会设置一个signal,SIGALRM,警告信号。

if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
         (*p)->state == TASK_INTERRUPTIBLE)
      (*p)->state = TASK_RUNNING;    //置为就绪(可执行)状态。
}

if()里面,信号不为空,并且去除掉不能引发进程就绪状态的阻塞信号,而且该进程为可中断睡眠状态,则将进程状态置为就绪状态

也就是,如果进程为可中断睡眠状态,则如果该进程有非屏蔽信号出现就将进程的状态设置为就绪状态。

可以看到上面说的代码都在for循环下的,所以如果各个进程都有此条件,那么它的状态都可以设置为就绪状态

 /* 这里是调度程序的主要部分 */
	while (1)
	{
		c = -1;
		next = 0;
		i = NR_TASKS;
		p = &task[NR_TASKS];
// 这段代码也是从任务数组的最后一个任务开始循环处理,并跳过不含任务的数组槽。比较每个就绪
// 状态任务的counter(任务运行时间的递减滴答计数)值,哪一个值大,运行时间还不长,next 就
// 指向哪个的任务号。
		while (--i)
		{
			if (!*--p)
				continue;
			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
				c = (*p)->counter, next = i;
		}
      // 如果比较得出有counter 值大于0 的结果,则退出124 行开始的循环,执行任务切换(141 行)。
		if (c)
			break;
      // 否则就根据每个任务的优先权值,更新每一个任务的counter 值,然后回到125 行重新比较。
      // counter 值的计算方式为counter = counter /2 + priority。[右边counter=0??]
		for (p = &LAST_TASK; p > &FIRST_TASK; --p)
			if (*p)
				(*p)->counter = ((*p)->counter >> 1) + (*p)->priority;
	}

这一段代码就是调度的主体。其实很简单,可能是0.11的代码吧,也就是进行了一个比较排序,找出counter最大的进程next,后面就是如果这个counter>0,就可以break,直接进行下面的switch_to(next);如果不大于0,则根据进程的优先级再进行调度。

 

进程切换

switch_to(next);进程切换函数

extern _inline void switch_to(int n) 
{
	unsigned short __tmp;
	__tmp = (unsigned short)_TSS(n);

	_asm {
		mov ebx, offset task
		mov eax, n
		mov ecx, [ebx+eax*4]
		cmp ecx, current/* 任务n 是当前任务吗?(current ==task[n]?) */ 
		je l1 /* 是,则什么都不做,退出。*/ 
		xchg ecx,current/* current = task[n]; */
		/*执行长跳转,造成任务切换 (头大了很长时间,多多包涵)*/
		mov ax, __tmp
		mov word ptr ds:[lcs],ax
		_emit 0xea
		_emit 0		// ip
		_emit 0 
		_emit 0 
		_emit 0
lcs:	_emit 0		// cs
		_emit 0
// 在任务切换回来后才会继续执行下面的语句。
		cmp last_task_used_math,ecx /* 新任务上次使用过协处理器吗?*/
		jne l1
		clts/* 新任务上次使用过协处理器,则清cr0 的TS 标志。*/
	}
l1: ;
}

switch_to(n)

把进程切换为当前进程

          1.将需要切换的进程赋值给当前进程指针

          2.进行进程的上下文(程序运行时CPU的特殊寄存器 通用寄存器(TSS)等信息+当前堆栈中的信息)切换 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值