对于linux 0.11版本中睡眠和唤醒函数的理解

时间:2022-12-03 14:37:10

sleep_on()函数主要是为了在一个进程所请求的资源忙的时候进行进程调度,并把该进程放到等待队列上等待一段时间。代码如下:

void sleep_on(struct task_struct **p)
{
	struct task_struct *tmp;

	if (!p)
		return;
	if (current == &(init_task.task))
		panic("task[0] trying to sleep");
	tmp = *p;
	*p = current;
	current->state = TASK_UNINTERRUPTIBLE;
	schedule();
	if (tmp)
		tmp->state=0;
}

分析:首先应该理解下这个tmp的作用,在这段函数中主要用到三个指针:*p,current和tmp,其中*p指向等待队列的头,也就是最近被放入等待队列的任务,current指向当前任务,tmp是在任务内核堆栈上建立的一个临时指针。在sleep的入口处,会让

       tmp = *p;

       *p= current;

这样的话tmp指向原来的头,*p指向新的头,这样就相当于串联起一个链表一样的结构,通过*p可以找到第一个等待任务,再通过第一个任务里面的tmp结构又可以找到下一个等待任务。下图可以帮助理解:

        对于linux 0.11版本中睡眠和唤醒函数的理解

之后执行current->state= TASK_UNINTERRUPTIBLE,将当前任务的状态改为TASK_UNINTERRUPTIBLE后进行任务调度,在执行任务调度的时候,当前的执行环境都会被保存起来,也就是后面的

         if (tmp)

              tmp->state=0;

还不会执行,直到当前任务被唤醒之后,才会执行,因为每个等待队列中都保存了这个指向环境,当等待队列头被唤醒后,就会执行上面的代码,而tmp指向下一个等待任务,这样下一个也被唤醒了,一次类推,整个等待队列中的任务都被唤醒,这里的唤醒指进程状态编程可执行。

另一个睡眠代码是interruptible_sleep_on(),调用这个函数的进程的休眠状态可以被信号唤醒。代码如下:

void interruptible_sleep_on(struct task_struct **p)
{
	struct task_struct *tmp;

	if (!p)
		return;
	if (current == &(init_task.task))
		panic("task[0] trying to sleep");
	tmp=*p;
	*p=current;
repeat:	current->state = TASK_INTERRUPTIBLE;
	schedule();
	if (*p && *p != current) {
		(**p).state=0;
		goto repeat;
	}
	*p=NULL;
	if (tmp)
		tmp->state=0;
}

        在这个代码里面,多了一个判断,if (*p && *p != current),使用这个来判断当前进程是不是等待队列的头,如果不是则将当前进程的状态置为TASK_INTERRUPTIBLE,然后重新进行调度。

        说下我的理解,在sleep_on中进程的状态被置为TASK_UNINTERRUPTIBLE,这样的话该等待队列的进程都不能被信号唤醒,只有调用了wake_up才可以,这样的话必定唤醒的是等待队列的头,而在interruptible_sleep_on中,进程的状态会被设置为TASK_INTERRUPTIBLE,这样的话等待队列中的某个进程就能够被信号量唤醒,它可能不是头,假如它被执行,那链接在它后面的睡眠进程都将被唤醒,而它之前的也在等待队列中的进程却没有被唤醒,而前一个等待进程的tmp仍旧指向这个刚被唤醒的进程,这样应该算错误了,所以只能从头结点开始。

wake_up的代码如下:

void wake_up(struct task_struct **p)
{
	if (p && *p) {
		(**p).state=0;
		*p=NULL;
	}
}

它是从等待队列的头结点开始,将第一个等待进程的状态置为可运行。