等待队列
二.等待队列基础知识
当我们进程去访问设备的时候,经常需要等待有特定事件发生以后在继续往下运行,这 个时候就需要在驱动里面实现当条件不满足的时候进程休眠,当条件满足的时候在由内核唤醒 进程。 那么等待队列就实现了在事件上的条件等待。
<1> 等待队列头 等待队列头就是一个等待队列的头部, 每个访问设备的进程都是一个队列项, 当设备 不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。 等待队列头使用结构体 wait_queue_head_t 来表示,这个结构体定义在文件 include/linux/wait 里面,结构体内容如下:
struct __wait_queue_head {
spinlock_t lock; // 自旋锁
struct list_head task_list; // 链表头
};
typedef struct __wait_queue_head wait_queue_head_t;
类型名是 wait_queue_head_t , 只需要记住这个即可。
定义一个等待队列头: wait_queue_head_t test_wq; // 定义一个等待队列的头
定义等待队列头以后需要初始化,可以使用 init_waitqueue_head 函数初始化等待队列头, 函数原型如下:
void init_waitqueue_head(wait_queue_head_t *q)
也可以使用宏 DECLARE_WAIT_QUEUE_HEAD 来一次性完成等待队列头的定义和初始化。
DECLARE_WAIT_QUEUE_HEAD (wait_queue_head_t *q);
三.等待队列相关函数
<1>init_waitqueue_head 宏
原型: void init_waitqueue_head(wait_queue_head_t *q)
作用:动态初始化等待队列头结构
参数:
q 是 wait_queue_head_t 指针
<2>wait_event 宏
原型: wait_event(wq, condition)
功能:不可中断的阻塞等待,让调用进程进入不可中断的睡眠状态,在等待队列里面睡眠直到 condition 变成真,被内核唤醒。
参数:
wq : wait_queue_head_t 类型变量。
condition 为等待的条件,为假时才可以进入休眠
注意:调用的时要确认 condition 值是真还是假,如果调用 condition 为真,则不会休眠。
<3>wait_event_interruptible 宏
原型: wait_event_interruptible(wq, condition)
功能:可中断的阻塞等待,让调用进程进入可中断的睡眠状态,直到 condition 变成真被内核 唤醒或被信号打断唤醒。
参数:
wq : wait_queue_head_t 类型变量。
condition 为等待的条件,为假时才可以进入休眠
返回:判断 condition 是否为真 , 如果为真则返回 , 否则检查如果进程是被信号唤醒 , 会返回 ERESTARTSYS 错误码 . 如果是 condition 为真 , 则 返回 0
<4>wake_up 宏
原型: wake_up(x)
功能:唤醒所有休眠进程
参数:
x : 等待队列头结构指针。
<5>wake_up_interruptible 宏
原型: wake_up_interruptible(x)
功能:唤醒可中断的休眠进程
参数:
x : 等待队列头结构指针。
实践课
定义并且初始化等待队列:
DECLARE_WAIT_QUEUE_HEADER(key_wq);
int wq_flags = 0;//定义标志位
中断函数:
irq_handler_t test_key(int irq, void *args)//中断处理函数
{
value = !value;
wq_flags = 1;
wake_up(&key_wq);//唤醒进程
return IRQ_HANDLED;
}
传送到应用层的代码
int misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
wait_event_interruptible(key_wq,wq_flags);
if(copy_to_user(ubuf,value,sizeof(value))!=0)
{
printk("copy to user error\n");
return -1;
}
wq_flags = 0;
return 0;
}
实验结果:阻塞打印。
未完待续。。。