基础
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");