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

一些概念

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

代码解析

  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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值