linux环形缓冲区kfifo实践2:配合等待队列使用

基础

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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");

测试结果:

linux环形缓冲区kfifo实践2:配合等待队列使用_第1张图片

小结 

你可能感兴趣的:(linux内核,linux,kfifo,等待队列)