Linux内核的 等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以用来同步对系统资源的访问、异步事件通知、跨进程通信等。如下图所示,
作用和功能:
等待队列用于使进程等待某一特定的事件发生而无需频繁的轮询。
在不需要执行任务的时候,我们就让任务进程休眠,直到条件改变时,我们再唤醒他,执行完毕后继续让它睡眠
在Linux中,等待队列以循环链表为基础结构,包括两种数据结构:
等待队列头(wait queue head) 和 等待队列元素(wait queue) ,整个等待队列由等待队列头进行管理 。使用到的数据结构如下
struct __wait_queue_head { //queue head
spinlock_t lock;
struct list_head task_list;//head
};
typedef struct __wait_queue_head wait_queue_head_t;
struct __wait_queue {//entry 队列元素
unsigned int flags;
void *private;-------------→ 关键,是task_struct 进程指针
wait_queue_func_t func;-----》指向等待队列被唤醒时的回调的唤醒函数
struct list_head task_list;
};
使用例子1
DECLARE_WAIT_QUEUE_HEAD(queue);
DECLARE_WAITQUEUE(wait, current);
for (;;) {
add_wait_queue(&queue, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (condition)
break;
schedule();
remove_wait_queue(&queue, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
}
set_current_state(TASK_RUNNING);
A sleep coded in this manner is safe against missed wakeups. It is also a fair amount of error-prone boilerplate code for a very common situation. In 2.6, a set of helper functions has been added which makes this task easier. The modern equivalent of the above code would look like:
使用例子2:
DECLARE_WAIT_QUEUE_HEAD(queue);
DEFINE_WAIT(wait);
while (! condition) {
prepare_to_wait(&queue, &wait, TASK_INTERRUPTIBLE);
if (! condition)
schedule();
finish_wait(&queue, &wait)
}
使用例子3:
1)首先初始化等待队列头
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
2)调用wait_event_interruptible如果condition为1则说明需要的条件都满足进程不睡眠,如果condition不为1则说明需要的条件不满足,进程立即进入睡眠等待被唤醒,被唤醒后则开始执行任务,任务执行完毕继续进入睡眠
if( wait_event_interruptible(queue, condition) )
{
//执行任务
}
3)当需要等待中的进程执行任务的时候,则我们唤醒wait_queue
wake_up_interruptible(wait_queue_head_t *queue)
内核mmc驱动,实际mmc host使用之前需要请求host,把当前进程设置等待host可用
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
DECLARE_WAITQUEUE(wait, current);//定义等待队列元素,关联当前进程,唤醒回调函数为default_wake_function()
unsigned long flags;
int stop;
bool pm = false;
might_sleep();
add_wait_queue(&host->wq, &wait);//将当前等待队列元素加入到等待队列host->wq中
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);// 当前进程状态设置为 TASK_UPINTERRUPTIBLE,此时仍未让出CPU
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)// 真正让出CPU前判断等待的资源是否已经得到
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();// 调用调度器,让出CPU,当前进程可进入休眠
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
if (!stop) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
if (host->claim_cnt == 1)
pm = true;
} else
wake_up(&host->wq);// 可利用abort参数执行一次等待队列唤醒工作
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);/// 等待队列结束,将等待队列元素从等待队列中移除
if (pm)
pm_runtime_get_sync(mmc_dev(host));
return stop;
}
唤醒在下面的函数中
/**
* mmc_release_host - release a host
* @host: mmc host to release
*
* Release a MMC host, allowing others to claim the host
* for their operations.
*/
void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
WARN_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
spin_unlock_irqrestore(&host->lock, flags);
} else {
host->claimed = 0;//=0代表没有进程请求host,等待进程可以使用host
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);// 唤醒等待队列host->wq上的所有进程
pm_runtime_mark_last_busy(mmc_dev(host));
pm_runtime_put_autosuspend(mmc_dev(host));
}
}
一般配套使用 ,使用前申请,使用后释放
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
.....
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
host->ops->get_cd(host) == 0) {
mmc_claim_host(host);---------申请
mmc_power_off(host);
mmc_release_host(host);----释放
goto out;
}
mmc_claim_host(host);-----申请
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
extend_wakelock = true;
break;
}
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);-----释放
out:
if (extend_wakelock)
wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
else
wake_unlock(&mmc_delayed_work_wake_lock);
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
实际例子
include<linux/module.h>
#include<linux/sched.h>
#include<linux/list.h>
#include<linux/kthread.h>
#include<linux/wait.h>
#include<linux/types.h>
#include<linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL");
int condition =0 ;
DECLARE_WAIT_QUEUE_HEAD(queue);
int test_wait(void * argc)
{
//DECLARE_WAIT_QUEUE_HEAD(queue);
DECLARE_WAITQUEUE(wait, current);
printk("in thread \n");
//add_wait_queue(&queue, &wait);
for (;;) {
add_wait_queue(&queue, &wait);
set_current_state(TASK_INTERRUPTIBLE);
printk("in thead,condition is %d\n",condition);
//if (condition) break;
schedule();
printk("has wakeup ,so continue \n");
remove_wait_queue(&queue, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
}
set_current_state(TASK_RUNNING);
return 0;
}
static int mytest_proc_show(struct seq_file *seq, void *v)
{
seq_puts(seq, condition ? "true\n" : "false\n");
return 0;
}
static int mytest_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, mytest_proc_show, inode->i_private);
}
static ssize_t mytest_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char mode;
printk("proc write\n");
if (count > 0) {
if (get_user(mode, buffer))
return -EFAULT;
printk("mode is %c \n",mode);
condition = (mode != '0');
printk("end write,condition is %d \n",condition);
//if(condition)
wake_up_interruptible(&queue);
}
return count;
}
static const struct file_operations mytest_proc_fops = {
.open = mytest_proc_open,
.read = seq_read,
.write = mytest_proc_write,
.llseek = seq_lseek,
.release = single_release,
};
struct proc_dir_entry *mytest_file;
struct task_struct *thread1;
static int __init prepare_to_wait_init(void)
{
//struct task_struct *thread1;
printk("module init begin \n");
thread1 = kthread_run(test_wait, NULL, "wait and wakeup test");
if(thread1)
printk("creat thread1 \n");
mytest_file = proc_create("mytest", 0x0666, NULL, &mytest_proc_fops);
return 0;
}
static void __exit prepare_to_wait_exit(void)
{
printk("\n rmmod ,Goodbye prepare_to_wait\n");
if(mytest_file)
proc_remove(mytest_file);
}
module_init(prepare_to_wait_init);
module_exit(prepare_to_wait_exit);
参考: