Linux device driver ---sleeping(睡眠唤醒)

In Linux, a wait queue is managed by means of a “wait queue head,” a
structure of type wait_queue_head_t, which is defined in
<linux/wait.h>. A wait queue head can be defined and initialized
statically with:

在Linux中等待队列是由一个等待队列头管理的。

DECLARE_WAIT_QUEUE_HEAD(name);

or dynamicly as follows:

wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);

When a process sleeps, it does so in expectation that some condition
will become true in the future. As we noted before, any process that
sleeps must check to be sure that the condition it was waiting for is
really true when it wakes up again. The simplest way of sleeping in
the Linux kernel is a macro called wait_event (with a few
variants); it combines handling the details of sleeping with a check
on the conditiona process is waiting for. The forms of wait_event
are:

当一个进程睡眠时,它还是会期望一些条件在未来会改变。
当一个睡眠的进程被再次唤醒时,它必须先检查一下条件以确保它等待的条件是否真的发生了。

wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)

In all of the above forms,
queue is the wait queue head to use. Notice that it is passed “by value.” The condition is an arbitrary boolean expression that is
evaluated by the macro before and after sleeping; until condition
evaluates to a true value, the process continues to sleep.

Note that condition may be evaluated an arbitrary number of times, so
it should not have any side effects.

The preferred alternative is wait_event_interruptible which can be
interrupted by signals. This version returns an integer value that you
should check; a nonzero value means your sleep was interrupted by some
sort of signal, and your driver should probably return -ERESTARTSYS.

wait_event_interruptible 可以被一个信号中断。
该函数会返回一个整型,你必须去检查一下,非零值意味着睡眠被一些信号打断了。
当你调用该函数时,它会去检查一下当前的执行条件是否为真,当为真时,该进程不会因为等待运行条件而被挂起,会继续执行。
如果此时添加为假,其会睡眠。

成功地唤醒一个被wait_event_interruptible()的进程,需要满足:
1)condition为真的前提下
2)调用wake_up()。

**原因:
1. 一个进程睡眠时,本进程将自动再次被设
置为TASK_INTERRUPTIBLE状态;该进程会被从运行队列中移除,移动到到之前申明等待队列中,其再不会被运行;且这段代码会被放到一个由condition 控制的
for(;;)
{
if(condition=true)
break;
}
(类似的)循环中,此时该循环并没有执行 。
2. 而wake_up的作用就是将一个等待队列中的进程放到执行队列中,这时,该进程会重新运行,而其被condition的循环阻塞,所以这个运行起来的进程还会去检查一下condition,为真则跳出死循环,继续执行该进程。
3. 如果wake_up时条件condition不满足,本进程将自动再次被设
置为TASK_INTERRUPTIBLE状态,接下来执行schedule()的结果是再次被
从runqueue队列中删除
**

The final versions (wait_event_timeout and
wait_event_interruptible_timeout) wait for a limited time; after that
time period (expressed in jiffies) expires, the macros return with a
value of 0 regardless of how condition evaluates.

The other half of the picture, of course, is waking up. Some other
thread of execution (a different process, or an ***interrupt
handler***, perhaps) has to perform the wakeup for you, since your
process is, of course, asleep. The basic function that wakes up
sleeping processes is called wake_up. It comes in several forms (but
we look at only two of them now):

void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);

wake_up wakes up all processes waiting on the given queue (though the
situation is a little more complicated than that, as we will see
later). The other form (wake_up_interruptible) restricts itself
to processes performing an interruptible sleep. In general, the two
are indistinguishable (if you are using interruptible sleeps); in
practice, the convention is to use wake_up if you are using
wait_event and wake_up_interruptible if you use
wait_event_interruptible.

原型

/**
 * wait_event_interruptible - sleep until a condition gets true
 * @wq: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 *
 * The process is put to sleep (TASK_INTERRUPTIBLE) until the
 * @condition evaluates to true or a signal is received.
 * The @condition is checked each time the waitqueue @wq is woken up.
 * ***该进程会被条件或者信号唤醒。条件的检查会在每次wake_up队列时;***
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 *
 * The function will return -ERESTARTSYS if it was interrupted by a
 * signal and 0 if @condition evaluated to true.
 * 被信号量和条件唤醒会返回不同的值
 */
#define wait_event_interruptible(wq, condition)             \
({                                  \
    int __ret = 0;                          \
    if (!(condition))       
    /***当你调用该函数时,它会去检查一下当前的执行条件是否为真,当为真时,该进程不会因为等待运行条件而被挂起,会继续执行。如果此时添加为假,其会睡眠。***/             \
        __wait_event_interruptible(wq, condition, __ret);   \
    __ret;                              \
})

#define __wait_event_interruptible_timeout(wq, condition, ret)      \
do {                                    \
    DEFINE_WAIT(__wait);    /*系统自己定义一个等待*/                  \
                                    \
    for (;;) {                          \
        prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);  \
        if (condition)                      \
            break;                      \
        if (!signal_pending(current)) { /***会被一个信号悬挂起来,有信号来时也会跳出该语句,继续执行。***/   \
            ret = schedule_timeout(ret);                    \
            if (!ret)                   \
                break;                  \
            continue;                   \
        }                           \
        ret = -ERESTARTSYS;                 \
        break;                          \
    }                               \
    finish_wait(&wq, &__wait);  /*结束睡眠,从系统自定义的等待中退出,放到我们自己定义的等待队列头*/                \
} while (0)

例子

static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);
**wait_event_interruptible(wq, flag != 0);**
flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
flag = 1;
**wake_up_interruptible(&wq);**
return count; /* succeed, to avoid retrial */
}

Note the use of the flag variable in this example. Since wait_event_interruptible checks for a condition that must become true, we use flag to create that condition.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值