wait_queue如何使用

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);

参考:

Linux 阻塞与唤醒实现原理 - zbs666 - 博客园

Linux等待队列(Wait Queue) - huey_x - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值