您的位置:首页 > 运维架构 > Linux

Linux下无效唤醒的应用

2015-11-04 22:04 543 查看
之前看过百度文库中一篇文章“Linux进程的睡眠和唤醒”,但是不是特别理解。
http://wenku.baidu.com/link?url=nXPCC19-zZWPU-ccwnO4ho-6zEuHsCdRn-56gJNbNGN49bjPt8qYuOa6qSI2NCb1s1coom2iob6N_7axaQxl8oxhVDuCznuMu0VfjnHNJ3u
今天看到mtdblock.c文件中的代码,才懂得其中的奥妙。

static void erase_callback(struct erase_info *done)

{

wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;

wake_up(wait_q);

}

static int erase_write (struct mtd_info *mtd, unsigned long pos,

int len, const char *buf)

{

struct erase_info erase;

DECLARE_WAITQUEUE(wait, current);

wait_queue_head_t wait_q;

size_t retlen;

int ret;

/*

* First, let's erase the flash block.

*/

init_waitqueue_head(&wait_q);

erase.mtd = mtd;

erase.callback = erase_callback;

erase.addr = pos;

erase.len = len;

erase.priv = (u_long)&wait_q;

set_current_state(TASK_INTERRUPTIBLE);

add_wait_queue(&wait_q, &wait);

ret = mtd_erase(mtd, &erase);

if (ret) {

set_current_state(TASK_RUNNING);

remove_wait_queue(&wait_q, &wait);

printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " "on \"%s\" failed\n",

pos, len, mtd->name);

return ret;

}

schedule(); /* Wait for erase to finish. */

remove_wait_queue(&wait_q, &wait);

/*

* Next, write the data to flash.

*/

....

}

猛地一看代码确实不理解,一会INTERRUPT一会RUNNING。

深入分析,其实就是避免无效唤醒的策略。

首先调用
DECLARE_WAITQUEUE()
创建一
个等待队列的项,然后调用
add_wait_queue()
把自己加入到等待队列中,并且将进程的状态设置为
TASK_INTERRUPTIBLE
或者
TASK_INTERRUPTIBLE
。然后循环检查条件是否为真:如果是的话就没有必要睡眠,如果条件不为真,就调用
schedule()
。当进程检查
的条件满足后,进程又将自己设置为
TASK_RUNNING
并调用
remove_wait_queue()
将自己移出等待队列。

从上面可以看到,
Linux
的内核代码维护者也是在进程检查条件之前就设置进程的状态为睡眠状态,

然后才循环检查条件。如果在进程开始睡眠之前条件就已经达成了,那么循环会退出并用
set_current_state()
将自己的状态设置为
就绪,这
具体步骤如下:
1. 首先调用DECLARE_WAITQUEUE()创建一个等待队列的项

2. 调用add_wait_queue()把自己加入到等待队列中,并且将进程的状态设置为TASK_INTERRUPTIBLE或者TASK_INTERRUPTIBLE。

3. 检查条件是否为真:如果是的话就没有必要睡眠,进程又将自己设置为TASK_RUNNING 并调用remove_wait_queue()将自己移出等待队列。

4. 如果条件不为真,就调用schedule()。等待erase_callback函数执行唤醒自己。

在这里因为考虑到erase的时间过长,所以考虑到了进程调度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: