wait_event_interruptible简介
wait_event_interruptible(以及wait_event打头的其他变体)是Linux的wait queue机制提供的线程同步接口,它的定义如下
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
might_sleep(); \
if (!(condition)) \
__ret = __wait_event_interruptible(wq, condition); \
__ret; \
})
它的用法很简单,Linux Device Driver第三版
示例代码如下
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的发生,即其他线程将flag改成非0值
flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
踩坑1 我不想在condition里使用全局变量
定义全局变量flag太丑了,较好的做法是使用file结构体的private_data字段来获取设备的控制结构体,这样 condition表达式就可以使用局部变量
了:
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
struct tmc_dev *dev = filp->private_data;
printk(KERN_DEBUG "process %i (%s) going to sleep\n", current->pid, current->comm);
wait_event_interruptible(wq, dev->flag != 0); //等待flag != 0的发生,即其他线程将flag改成非0值
dev->flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
虽然dev指向的对象大概率也是全局变量,至少是分配在堆上的,但sleepy_read 函数用局部变量dev可以避免将来全局变量改名对自身的影响。
可能有人会担心线程sleep时dev指针会无效,从而导致condition的评估失败,不用担心,condition被评估时线程一定是处于RUNNING态的,此时线程的上下文(包括函数调用栈)都已被恢复,所以dev指针是有效的。为啥这么说?因为休眠线程是被内核或别的线程通过wake_up_interruptible()唤醒,唤醒时必然恢复上下文。
踩坑2 我的condition表达式太长,一行放不下
可以把condition表达式拆成多行
,但要记 得在行尾添加续行符\
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
struct tmc_dev *dev = filp->private_data;
printk(KERN_DEBUG "process %i (%s) going to sleep\n", current->pid, current->comm);
wait_event_interruptible(wq, dev->flag != 0 && \
dev->state == STATE_COMPLETE); //等待flag != 0且dev->state == STATE_COMPLETE的发生
dev->flag = 0;
dev->state == STATE_IDLE;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
为啥要加续行符?因为wait_event_interruptible()其实是个宏函数,不论宏函数还是普通常量宏,都是宏定义,而C语言只允许单行的宏定义,要想多行,必须在行尾加续行符。如果不加,其行为是未定义的,Linux内核也hold不住。