基础
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
初始化等待队列:init_waitqueue_head
深挖init_waitqueue_head宏的定义可知,传递给它的参数q是一个wait_queue_head_t类型的指针。
init_waitqueue_head的参数q是一个指针
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);
等待队列
/**
* wait_event_interruptible - sleep until a condition gets true
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
* @condition evaluates to true or a signal is received.
* The @condition is checked each time the waitqueue @wq is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*
* The function will return -ERESTARTSYS if it was interrupted by a
* signal and 0 if @condition evaluated to true.
*/
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
might_sleep(); \
if (!(condition)) \
__ret = __wait_event_interruptible(wq, condition); \
__ret; \
})
/*
* The below macro ___wait_event() has an explicit shadow of the __ret
* variable when used from the wait_event_*() macros.
*
* This is so that both can use the ___wait_cond_timeout() construct
* to wrap the condition.
*
* The type inconsistency of the wait_event_*() __ret variable is also
* on purpose; we use long where we can return timeout values and int
* otherwise.
*/
#define ___wait_event(wq, condition, state, exclusive, ret, cmd) \
({ \
__label__ __out; \
wait_queue_t __wait; \
long __ret = ret; /* explicit shadow */ \
\
INIT_LIST_HEAD(&__wait.task_list); \
if (exclusive) \
__wait.flags = WQ_FLAG_EXCLUSIVE; \
else \
__wait.flags = 0; \
\
for (;;) { \
long __int = prepare_to_wait_event(&wq, &__wait, state);\
\
if (condition) \
break; \
\
if (___wait_is_interruptible(state) && __int) { \
__ret = __int; \
if (exclusive) { \
abort_exclusive_wait(&wq, &__wait, \
state, NULL); \
goto __out; \
} \
break; \
} \
\
cmd; \
} \
finish_wait(&wq, &__wait); \
__out: __ret; \
})
long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state);
从wait_event_interruptible(wq, condition)到prepare_to_wait_event(&wq, &__wait, state),注意这里是&wq,又知prepare_to_wait_event函数的第一个参数是指针,所以,
从wait_event_interruptible宏的定义深挖可知,wait_event_interruptible的第一个参数不是指针,是传递的结构体变量。
wait_event_interruptible的第一个参数不是指针,是传递的结构体变量
wait_event_interruptible的第一个参数不是指针,是传递的结构体变量
唤醒队列
从wake_up_interruptible宏的定义向下深挖可知,wake_up_interruptible的第一个参数是指针
wake_up_interruptible的第一个参数是指针。
wake_up_interruptible的第一个参数是指针。
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
驱动代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#define DEBUG_INFO(format, ...) printk("%s:%d -- "format"\n",\
__func__,__LINE__,##__VA_ARGS__)
struct ch5_kfifo_struct{
struct miscdevice misc;
struct file_operations fops;
struct kfifo fifo;
char buf[64];
wait_queue_head_t read_queue;
wait_queue_head_t write_queue;
};
static int ch5_open (struct inode *inode, struct file *file){
struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)container_of(file->f_op,struct ch5_kfifo_struct,fops);
file->private_data = p;
DEBUG_INFO("major = %d, minor = %d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));
DEBUG_INFO("name = %s",p->misc.name);
return 0;
}
static int ch5_release (struct inode *inode, struct file *file){
DEBUG_INFO("close");
return 0;
}
static ssize_t ch5_read (struct file *file, char __user *buf, size_t size, loff_t *pos){
struct ch5_kfifo_struct *p __attribute__((unused)) = (struct ch5_kfifo_struct *)file->private_data;
int ret;
int actual_readed = 0;
if(kfifo_is_empty(&p->fifo)){
if(file->f_flags & O_NONBLOCK){
DEBUG_INFO("kfifo is null");
return -EAGAIN;
}
ret = wait_event_interruptible(p->read_queue,kfifo_is_empty(&p->fifo) == 0);
if(ret){
DEBUG_INFO("wait_event_interruptible error");
return ret;
}
DEBUG_INFO("");
}
ret = kfifo_to_user(&p->fifo, buf, size, &actual_readed);
if (ret){
DEBUG_INFO("kfifo_to_user error");
return -EIO;
}
DEBUG_INFO("size = %d,actual_readed = %d\n",size,actual_readed);
if (!kfifo_is_full(&p->fifo)){
wake_up_interruptible(&p->write_queue);
}
memset(p->buf,0,sizeof(p->buf));
ret = copy_from_user(p->buf, buf, actual_readed);
if(ret != 0){
DEBUG_INFO("copy_from_user error ret = %d\n",ret);
}else{
DEBUG_INFO("read p->buf = %s\n",p->buf);
}
*pos = *pos + actual_readed;
return actual_readed;
}
static ssize_t ch5_write (struct file *file, const char __user *buf, size_t size, loff_t* pos){
struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)file->private_data;
int actual_writed = 0;
int ret;
if(kfifo_is_full(&p->fifo)){
if(file->f_flags & O_NONBLOCK){
DEBUG_INFO("kfifo is full");
return -EAGAIN;
}
ret = wait_event_interruptible(p->write_queue, kfifo_is_full(&p->fifo) == 0);
if(ret){
DEBUG_INFO("wait_event_interruptible error");
return ret;
}
DEBUG_INFO("");
}
ret = kfifo_from_user(&p->fifo, buf, size, &actual_writed);
if (ret){
DEBUG_INFO("kfifo_from_user error");
return -EIO;
}
DEBUG_INFO("actual_writed = %d\n",actual_writed);
if (!kfifo_is_empty(&p->fifo)){
wake_up_interruptible(&p->read_queue);
}
memset(p->buf,0,sizeof(p->buf));
ret = copy_from_user(p->buf, buf, actual_writed);
if(ret != 0){
DEBUG_INFO("copy_from_user error ret = %d\n",ret);
}else{
DEBUG_INFO("write:p->buf = %s\n",p->buf);
}
*pos = *pos + actual_writed;
return actual_writed;
}
struct ch5_kfifo_struct ch5_kfifo = {
.misc = {
.name = "ch5-04-block",
.minor = MISC_DYNAMIC_MINOR,
},
.fops = {
.owner = THIS_MODULE,
.read = ch5_read,
.write = ch5_write,
.open = ch5_open,
.release = ch5_release,
},
};
static int __init ch5_init(void){
int ret = 0;
DEBUG_INFO("start init\n");
ch5_kfifo.misc.fops = &ch5_kfifo.fops;
ret = kfifo_alloc(&ch5_kfifo.fifo,
8,
GFP_KERNEL);
if (ret) {
DEBUG_INFO("kfifo_alloc error: %d\n", ret);
ret = -ENOMEM;
return ret;
}
DEBUG_INFO("kfifo_alloc size = %d",kfifo_avail(&ch5_kfifo.fifo));
init_waitqueue_head(&ch5_kfifo.read_queue);
init_waitqueue_head(&ch5_kfifo.write_queue);
ret = misc_register(&ch5_kfifo.misc);
if(ret < 0){
DEBUG_INFO("misc_register error: %d\n", ret);
return ret;
}
DEBUG_INFO("misc_register ok");
return 0;
}
static void __exit ch5_exit(void){
DEBUG_INFO("exit\n");
misc_deregister(&ch5_kfifo.misc);
kfifo_free(&ch5_kfifo.fifo);
}
module_init(ch5_init);
module_exit(ch5_exit);
MODULE_LICENSE("GPL");