《linux设备驱动开发详解》中支持阻塞操作的globalfifo设备驱动

来源:互联网 发布:淘宝卖家手机端装修 编辑:程序博客网 时间:2024/05/29 10:59

一些概念

阻塞操作:执行设备操作时,若不能获得资源,挂起进程(进入睡眠状态)直到满足可操作的条件后再进行操作。
非阻塞操作:执行设备操作时,若不能获得资源,要么放弃、要么不停查询。

代码解析

  1. 基于上文”《linux设备驱动开发详解》中globalmem代码笔记“的代码,只是将globalmem中的全局内存(devp所指)变成FIFO,并将globalmem重命名为”globalfifo”
  2. 当FIFO中有数据的时候,读进程才能把数据读出,且读取后的数据要从FIFO移除;当FIFO不是满的时候,写进程才能往FIFO中写数据
  3. 实现功能:读FIFO进程将唤醒写FIFO进程,写FIFO也将唤醒FIFO的进程
  4. 结构体的更改
struct globalfifo_dev{    struct cdev cdev;    unsigned int current_len;//FIFO中数据长度    unsigned char mem[GLOBALFIFO_SIZE];    struct mutex mutex;    /*****************************************    *增加两个等待队列头部,分别对应读和写    *******************************************/    wait_queue_head_t r_wait;    wait_queue_head_t w_wait;};struct globalfifo_dev *globalfifo_devp;

注:等待队列一般来实现阻塞进程的唤醒
5. 加载函数XXX_init

static int __init globalfifo_init(void){    //allocate dev_t    ...    //init mutex    ...    /***************************************    *初始化“等待队列头部”    ***************************************/    init_waitqueue_head(&globalfifo_devp->r_wait);    init_waitqueue_head(&globalfifo_devp->w_wait);    //register cdev    ...}module_init(globalfifo_init);

注:
“等待队列头部”与“等待队列元素”:“等待队列元素”是要添加到“等待队列头部”实现之后的唤醒
6. 读函数read: 读函数中需要唤醒写进程

static ssize_t globalfifo_read(struct file *filp, char __user *buff, size_t size, loff_t *ppos){    unsigned int count = size;    int ret = 0;    struct globalfifo_dev *dev = filp->private_data;    /*****************************************************    *定义并初始化“等待队列元素”wait    ******************************************************/    DECLARE_WAITQUEUE(wait, current);    mutex_lock(&dev->mutex);    /***************************************************    *将“等待队列元素”添加到“等待队列头元素”,此时并未休眠    ***************************************************/    add_wait_queue(&dev->r_wait, &wait);    while (dev->current_len == 0){    /*************************************************    *若是设置成非阻塞模式则直接跳出循环,避免阻塞    *************************************************        if (filp->f_flags & O_NONBLOCK){            ret = -EAGAIN;            goto out;        }        /**************************************        *标记浅度睡眠标志,并未睡眠        *****************************************/        __set_current_state(TASK_INTERRUPTIBLE);        /***************************************        *!!!!!!!重要:睡眠前,要释放锁         ***************************************/        mutex_unlock(&dev->mutex);        /************************************        *睡眠        ************************************/        schedule();        /***********************************        *浅睡眠有可能是被信号唤醒,而我们只需要写进程唤醒我们,所以需要判断是不是信号唤醒        ***********************************/        if (signal_pending(current)){            ret = -ERESTARTSYS;            goto out2;        }        mutex_lock(&dev->mutex);//不是信号唤醒,访问资源前需要加锁    }    if (count > dev->current_len)        count = dev->current_len;    if (copy_to_user(buff, dev->mem, count)){        ret = -EFAULT;        goto out;    }else{        memcpy(dev->mem, dev->mem+count, dev->current_len - count);//读完之后需要将未读数据往前挪        dev->current_len -= count;//已读的count字节的数据空出来了        printk(KERN_INFO"read %d bytes, current_len: %d\n", count, dev->current_len);        /**********************************************        *唤醒写进程        **********************************************        wake_up_interruptible(&dev->w_wait);        ret = count;    }out:    mutex_unlock(&dev->mutex);out2:    remove_wait_queue(&dev->w_wait, &wait);//将“等待队列元素”从“头部”删除    set_current_state(TASK_RUNNING);//设置成就绪状态    return ret;}

7.写函数write
与读函数差不多

static ssize_t globalfifo_write(struct file *filp, const char __user *buff, size_t size, loff_t *ppos){    unsigned int count = size;    int ret;    struct globalfifo_dev *dev = filp->private_data;    DECLARE_WAITQUEUE(wait, current);//    mutex_lock(&dev->mutex);    add_wait_queue(&dev->w_wait, &wait);//    while(dev->current_len == GLOBALFIFO_SIZE){        if (filp->f_flags & O_NONBLOCK){            ret = -EAGAIN;            goto out;        }        __set_current_state(TASK_INTERRUPTIBLE);//        mutex_unlock(&dev->mutex);        schedule();//        if (signal_pending(current)){            ret = -ERESTARTSYS;            goto out2;        }        mutex_lock(&dev->mutex);    }    if (count > GLOBALFIFO_SIZE - dev->current_len)//整个FIFO大小 减去 有效数据大小        count = GLOBALFIFO_SIZE - dev->current_len;    if (copy_from_user(dev->mem+dev->current_len, buff, count)){        ret = -EFAULT;        goto out;    }else    {        dev->current_len += count;        printk(KERN_INFO"written %d bytes, current_len:%d\n", count, dev->current_len);        wake_up_interruptible(&dev->r_wait);        ret = count;    }out:    mutex_unlock(&dev->mutex);out2:    remove_wait_queue(&dev->w_wait, &wait);    set_current_state(TASK_RUNNING);    return ret;}

注:
等待队列的模板

static ssize_t XXX_write(struct file *filp, const char __user *buff, size_t size, loff_t *ppos){    ...    DECLARE_WAITQUEUE(wait, current);//定义等待队列元素    add_wait_queue(&dev->w_wait, &wait);//添加等待元素到队列    /*等待设备缓冲区可写*/    do{       avail = device_writable(...);       if (avail < 0)       {           if (filp->f_flags & O_NONBLOCK){//阻塞            ret = -EAGAIN;            goto out;           }       }       __set_current_state(TASK_INTERRUPTIBLE);//改变进程状态       schedule();//调度其他进程       if (signal_pending(current)){//判断是否信号唤醒            ret = -ERESTARTSYS;            goto out2;        }     }while(avail<0);     device_write()//写数据out:    remove_wait_queue(&XXX_wait, &wait);    set_current_state(TASK_RUNNING);    return ret;}
阅读全文
0 0
原创粉丝点击