Linux 0.11学习笔记

关于sleep_on()函数的一点疑惑

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

// 若指针无效,则退出。(指针所指向的对象可以是NULL,但指针本身不应该为0).另外,如果
// 当前任务是任务0,则死机。因为任务0的运行不依赖自己的状态,所以内核代码把任务0置为
// 睡眠状态毫无意义。
if (!p)
	return;
if (current == &(init_task.task))
	panic("task[0] trying to sleep");
// 让tmp指向已经在等待队列上的任务(如果有的话),例如inode->i_wait.并且将睡眠队列头的
// 等等指针指向当前任务。这样就把当前任务插入到了*p的等待队列中。然后将当前任务置为
// 不可中断的等待状态,并执行重新调度。
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
// 只有当这个等待任务被唤醒时,调度程序才又返回到这里,表示本进程已被明确的唤醒(就
// 续态)。既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该该资源
// 的进程。该函数嵌套调用,也会嵌套唤醒所有等待该资源的进程。这里嵌套调用是指一个
// 进程调用了sleep_on()后就会在该函数中被切换掉,控制权呗转移到其他进程中。此时若有
// 进程也需要使用同一资源,那么也会使用同一个等待队列头指针作为参数调用sleep_on()函数,
// 并且也会陷入该函数而不会返回。只有当内核某处代码以队列头指针作为参数wake_up了队列,
// 那么当系统切换去执行头指针所指的进程A时,该进程才会继续执行下面的代码,把队列后一个
// 进程B置位就绪状态(唤醒)。而当轮到B进程执行时,它也才可能继续执行下面的代码。若它
// 后面还有等待的进程C,那它也会把C唤醒等。在这前面还应该添加一行:*p = tmp.
if (tmp)                    // 若在其前还有存在的等待的任务,则也将其置为就绪状态(唤醒).
	tmp->state=0;

}

假设有A,B,C,D四个任务在等待某个资源,它们通过调用sleep_on()函数依次进入到资源的等待队列wait中,此时队列情况是D-C-B-A,wait=D。
某个时刻,资源被释放,任务D被唤醒。等任务D被schedule()调度执行后,会从sleep_on()函数中的schedule()之后开始执行(等待队列中的任何任务被恢复时都会从这开始恢复执行),即:
*

p=tmp;//此时tmp=C
if (tmp)                    // 若在其前还有存在的等待的任务,则也将其置为就绪状态(唤醒).
		tmp->state=0;

此时,wait=C,等待队列中C-B-A,任务C被设置成可调度状态。但此时还在任务D中。
任务D继续执行,一般情况是将资源上锁,开始执行相关操作。假设D将会长时间占用资源。当任务C开始被调用时:

*p=tmp;//此时tmp=B
if (tmp)                    // 若在其前还有存在的等待的任务,则也将其置为就绪状态(唤醒).
		tmp->state=0;

此时wait=B,等待队列中B-A,任务B被设置成可调度状态。此时还在任务C中。
任务C继续执行,开始申请资源,但由于资源被任务D占用,任务C再次调用sleep_on(),将自己睡眠。例如buffer.c中,如果任务在sleep_on()中醒来,执行完后发现资源又被上锁,会再次调用sleep_on将自己睡眠。

static inline void wait_on_buffer(struct buffer_head * bh)
{
	cli();                          // 关中断
	while (bh->b_lock)              // 如果已被上锁则进程进入睡眠,等待其解锁
		sleep_on(&bh->b_wait);
	sti();                          // 开中断
}

C睡眠后,wait=C,睡眠队列为C-B-A。此时,C,A均不会被schedule调度,但B之前已经被C设置成了可调度。当B开始运行后,B将A设置成可调度,wait=A,等待队列只有A了,C被丢掉了。B继续申请资源,发现还是上锁,又将自己睡眠此时wait=B,等待队列B-A。当A运行后,wait=NULL,之后申请资源发现资源被锁,又将自己睡眠,此时wait=A,睡眠队列里只有A,B又被丢掉了。

我觉得可以在进入sleep_on后,先检查当前队列头的state是否是0,如果是0,则改为TASK_UNINTERRUPTIBLE,因为目前资源被锁住了,不能运行了。具体如下:

If(*p && *p->stat==0)
	*p->state= TASK_UNINTERRUPTIBLE;
tmp = *p;
	*p = current;
	current->state = TASK_UNINTERRUPTIBLE;
	schedule();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值