1.1-内核的进程调度和进程切换

1:代码环境

linux 0.11

2: 其他函数

2.1 显示进程show_task()

void show_task(int nr,struct task_struct * p)//nr为pid,代表第几个进程
{
	int i,j = 4096-sizeof(struct task_struct); //找到栈堆的大小

	printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);//打印pid 和状态
	i=0;
	while (i<j && !((char *)(p+1))[i])
		i++;
	printk("%d (of %d) chars free in kernel stack\n\r",i,j);//打印栈的内容
}

2.2 协处理器函数 math_state_restore()

完成进程切换时,协处理器的内容切换

/*
 *  'math_state_restore()' saves the current math information in the
 * old math state array, and gets the new ones from the current task
 */
void math_state_restore()
{
	if (last_task_used_math == current)
		return;
	__asm__("fwait");
	if (last_task_used_math) {
		__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
	}
	last_task_used_math=current;
	if (current->used_math) {
		__asm__("frstor %0"::"m" (current->tss.i387));
	} else {
		__asm__("fninit"::);
		current->used_math=1;
	}
}

3: 进程调度

3.1 调度函数schedule()

进程的状态

#define TASK_RUNNING		0   可运行状态,就绪态
#define TASK_INTERRUPTIBLE	1  可中断睡眠状态(可以被信号中断,使其变成runing)
#define TASK_UNINTERRUPTIBLE	2 不可中断睡眠状态
//1 2 可以通过sleep_on函数改变成这种状态
#define TASK_ZOMBIE		3 僵死状态, 进程已经停止运行,父进程没有把它清空。直到子进程向父进程发送信号,父进程会使用waitpid()来接收信号
#define TASK_STOPPED		4 暂停状态 (收到暂停信号 SIGSTOP SIGSTP..
/*
 *  'schedule()' is the scheduler function. This is GOOD CODE! There
 * probably won't be any reason to change this, as it should work well
 * in all circumstances (ie gives IO-bound processes good response etc).
 * The one thing you might take a look at is the signal-handler code here.
 *
 *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
 * tasks can run. It can not be killed, and it cannot sleep. The 'state'
 * information in task[0] is never used.
 */
void schedule(void)
{
	int i,next,c;
	struct task_struct ** p; //用于指向进程数组的地址

/* check alarm, wake up any interruptible tasks that have got a signal */

	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) //指向数组的最后位置,从后向前
		if (*p) {
			if ((*p)->alarm && (*p)->alarm < jiffies) { //alarm不为0,并且当jiffies的大于了arlarm,说明时间到达
					(*p)->signal |= (1<<(SIGALRM-1)); //设置警告信号SIGALRM
					(*p)->alarm = 0;//清空alem
				}
			if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
			(*p)->state==TASK_INTERRUPTIBLE)//((信号存在 & 信号不是KILL和STOP &信号是不阻塞的)&& 进程是可中断的睡眠状态)
				(*p)->state=TASK_RUNNING; //把信号设置为可运行的状态
		}

/* this is the scheduler proper: */
//选择一个couter最大的进程结构体
	while (1) {
		c = -1;
		next = 0;
		i = NR_TASKS;
		p = &task[NR_TASKS]; //p指向最后的位置
		while (--i) {
			if (!*--p)//判断进程数组中指针是否为空
				continue;
			if ((*p)->state == TASK_RUNNING && (*p)->counter > c) //(进程可运行 && 并且进程中counter比当记录的最大值c大)
				c = (*p)->counter, next = i; //更新c的值为当前counter,记录当前计数到next
		}
		if (c) break; //如果c存在,就break出while(1),说明进程数组中还有可以执行的进程
		/*如果执行了下面函数,说明c为0,表示进程数组中,所有进程都执行完了,就会进行时间片的重分配*/
		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
			if (*p)
				(*p)->counter = ((*p)->counter >> 1) +
						(*p)->priority; //对counter赋值 counter = counter/2 + priority
	}
	switch_to(next);//切换进程
}

使用这个调度的函数如下:
在这里插入图片描述

3.2 切换函数switch_to(next);

将进程切换
1:将需要切换的进程赋值给current
2:进行进程的上下文切换 。上下文指的是CPU的特殊寄存器,通用寄存器 + 当前堆栈的信息

/*
 *	switch_to(n) should switch tasks to task nr n, first
 * checking that n isn't the current task, in which case it does nothing.
 * This also clears the TS-flag if the task we switched to has used
 * tha math co-processor latest.
 */
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \   //比较要切换的线程是不是当前线程  _current == %%ecx ?
	"je 1f\n\t" \ //为当前线程则不做任何事
	"movw %%dx,%1\n\t" \
	"xchgl %%ecx,_current\n\t" \ //将当前线程_current设置为要切换的线程 _current = %%ecx
	"ljmp %0\n\t" \
	"cmpl %%ecx,_last_task_used_math\n\t" \
	"jne 1f\n\t" \
	"clts\n" \
	"1:" \
	::"m" (*&__tmp.a),"m" (*&__tmp.b), \
	"d" (_TSS(n)),"c" ((long) task[n])); \
}

3.3 进程阻塞函数sleep_on()

当进程要访问的资源不存在的时候,进行调用

void sleep_on(struct task_struct **p) //传递的是当前睡眠进程链表的地址末尾地址
{
	struct task_struct *tmp; //创建一个只针tmp

	if (!p)
		return;
	if (current == &(init_task.task)) //当前进行为第一个进程时,就会打印panic信息, 这个进程不允许休眠
		panic("task[0] trying to sleep");
	tmp = *p;//让tmp指向是当前睡眠进程链表的地址末尾地址p
	*p = current;//p指向即将要休眠的当前进程成为新的睡眠进程链表的地址末尾地址
	current->state = TASK_UNINTERRUPTIBLE;//改变当前线程的状态为不可打断休眠状态,进行休眠
	schedule();//进行调度 并不会释放栈中的变量tmp
	if (tmp)
		tmp->state=0;
}

其中上面指针指向的,等待链表状态如下图所示
在这里插入图片描述

3.4 进程唤醒函数wake_up()

void wake_up(struct task_struct **p)
{
	if (p && *p) {
		(**p).state=0; //把进程状态修改成可运行状态
		*p=NULL;
	}
}

下面用inode节点中等待为例子说明其中的关系
1:第一个睡眠进程
在这里插入图片描述
2:第二个睡眠进程
在这里插入图片描述3:第三个睡眠进程
在这里插入图片描述
inode节点中的i_wait会指向一个睡眠进程的链表,链表之间通过每个进程内部tmp来进行链接

4:唤醒进程
在这里插入图片描述

唤醒时,链表中所有的进程都会被唤醒,并不是只唤醒链表中个一个或者部分。
但是唤醒后不一定本次inode资源可以满足所有的等待的进程,那么不能满足的进程会再次形成新的睡眠链表,还是由inode中的i_wait来指向。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值