等待队列概念以及使用

等待队列概念以及使用

  • 1.等待队列 wait_queue_head_t
  • 2.使用过程
  • 3.简单例子
  • 4.总结

1.等待队列 wait_queue_head_t

	等待队列用于使得进程等待某一特定事件的发生,无需频繁的轮询,进程在等待周期中睡眠,当事件发生后由内核自动唤醒。
	
	等待队列是一种实现阻塞和唤醒的内核机制,很早就作为一个基本的功能单位出现在Linux内核中,它以队列为基础数据结构,
	
	与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。

2.使用过程

	A、定义和初始化等待队列头
		DECLARE_WAIT_QUEUE_HEAD(wait);//初始化一个等待队列头 wait
		或者动态地, 如下:
			wait_queue_head_t wait;
			init_waitqueue_head(&wait);
	
	B、休眠进程
		wait_event(wait, condition) //将当前进程的状态设置为 TASK_UNINTERRUPTIBLE
		wait_event_interruptible(wait, condition) //TASK_INTERRUPTIBLE    
		wait_event_timeout(wait, condition, timeout) //TASK_UNINTERRUPTIBLE  
		wait_event_interruptible_timeout(wait, condition, timeout) //TASK_INTERRUPTIBLE    

		备注:
			wait 是等待队列头, condition 是条件,如果调用 wait_event 前 condition == 0 ,则调用 wait_event 之后,当前			
			进程就会休眠。
			
			TASK_INTERRUPTIBLE 与 TASK_UNINTERRUPTIBLE 区别在于,它的休眠是否会被信号打断,别的进程发来一个信号比如 kill ,
			TASK_INTERRUPTIBLE 就会醒来去处理。然而 TASK_UNINTERRUPTIBLE 不会。schedule(),进程调度,而 schedule_timeout()
			进行调度之后,一定时间后自动唤醒。
			
	C、唤醒进程
		void wake_up(wait_queue_head_t *wait);
		void wake_up_interruptible(wait_queue_head_t *wait);
		
		备注:	
			对应于不同的进程状态,使用不同的唤醒函数。比如你调用 wake_up 去唤醒一个使用 wait_event 等,进入休眠的进程,唤醒
			之后,它会判断 condition 是否为真,如果还是假的继续睡眠。

3.简单例子

	#include 
	#include 
	#include 
	#include 
	#include 
	#include 
	
	MODULE_AUTHOR("Jimmy");
	MODULE_DESCRIPTION("wait queue example");
	MODULE_LICENSE("GPL");
	
	/* 等待队列操作_2: 定义条件标志位 */
	static int condition;
	
	static struct task_struct *task_1;
	static struct task_struct *task_2;
	static struct task_struct *task_3;
	
	/* 等待队列操作_1: 声明等在队列头 */
	DECLARE_WAIT_QUEUE_HEAD(wq);
	
	
	static int thread_func_1(void *data)
	{
		msleep(100);//延时100ms,使得这个进程的等待队列插入在整个链表的头部,最先被唤醒。所以先打印这个进程!
		
		/* 等待队列操作_3:判断 condition ,如果为 0 使进入休眠,为 1 时程序往下运行 */
		wait_event_interruptible(wq, condition);
		
		/* 等待队列操作_4:flag 置 0 */
		condition = 0;
		printk(">>>>>this task 1\n");
		
		do {
			msleep(1000);
		}while(!kthread_should_stop());
		
		return 1;
	}
	
	static int thread_func_2(void *data)
	{
			
		wait_event_interruptible(wq, condition);
		condition = 0;
		
		printk(">>>>>this task 2\n");
		
		do {
			msleep(1000);
		}while(!kthread_should_stop());
		
		return 2;
	}
	
	static int thread_func_3(void *data)
	{    
		msleep(2000);
		printk(">>>>>this task 3\n");
		
		/* 等待队列操作_5:flag 置 1 表示事件发生,唤醒休眠的等待队列 */
		condition = 1;
		wake_up_interruptible(&wq);
	
		msleep(2000);
		condition = 1;
		wake_up_interruptible(&wq);
		
		do {
			msleep(1000);
		}while(!kthread_should_stop());
			
		return 3;
	}
	
	static int __init mod_init(void)
	{
		condition = 0;
	
		task_1 = kthread_run(thread_func_1, NULL, "thread%d", 1);
		if (IS_ERR(task_1)) {
			printk("******create thread 1 failed\n");
		} else {
			printk("======success create thread 1\n");
		}
		
		task_2 = kthread_run(thread_func_2, NULL, "thread%d", 2);
		if (IS_ERR(task_2)) {
			printk("******create thread 2 failed\n");
		} else {
			printk("======success create thread 2\n");
		}
	
		task_3 = kthread_run(thread_func_3, NULL, "thread%d", 3);
		if (IS_ERR(task_2)) {
			printk("******create thread 3 failed\n");
		} else {
			printk("======success create thread 3\n");
		}
			
		return 0;
	}
	
	static void __exit mod_exit(void)
	{
		int ret;
		
		if (!IS_ERR(task_1)) {
			ret = kthread_stop(task_1);
			printk("<<<<<<<, ret);
		}
			
		if (!IS_ERR(task_2)) {
			ret = kthread_stop(task_2);
			printk("<<<<<<<, ret);
		}
	
		if (!IS_ERR(task_3)) {
			ret = kthread_stop(task_3);
			printk("<<<<<<<, ret);
		}
		
		return;
	}
	
	module_init(mod_init);
	module_exit(mod_exit);
备注:
	     注意:
        1、要从wait_event()函数跳出,需要两个条件。1、condition = 1; 2、在内核的另一个地方调用了wake_up()函数。
        2wake_up()每次只能唤醒一个进程,而且是从队列头开始唤醒的,而wait_event()函数每次会将新建的等待队列插
			到队列头,因此最后调用wait_event()函数的进程先被唤醒!如果要唤醒某个特定的进程,没有现成的函数,按
			照本人理解,只能使用wake_up_all()函数唤醒所有进程,然后在通过条件condition来控制(每个进程使用不同的
			变量来控制,在wake_up_all()函数后只将要唤醒的进程的变量置成真)

4.总结

	static DECLARE_WAIT_QUEUE_HEAD(bat_thread_wq); //定义和初始化等待队列头
	
	wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE));//休眠进程,条件置0
    bat_thread_timeout = KAL_FALSE;
	
	bat_thread_timeout = KAL_TRUE;//唤醒进程,当条件为置1时唤醒进程
	wake_up(&bat_thread_wq);

你可能感兴趣的:(Linux,Linux,kernel)