高级字符驱动之堵塞与非堵塞IO

/**
  *此实例涉及到线程的挂起与竞态,字符IO的堵塞与非堵塞
  */

struct scull_pipe {
    wait_queue_head_t inp, outp;
    char *buffer, *end;
    char *rp, wp;
    //int buffersize;
    int nreaders, nwriters;
    struct fasync_struct *async_queue;
    struct semaphore sem;
    struct cdev cdev;
};


/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 * /inlcude/linux/kernel.h
 *
 */
/*#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
*/

static ssize_t scull_p_read (struct file *filp, char __user *buffer, size_t count, loff_t *f_pos)
{
    /*得到指向结构体scull_pipe的指针*/
    /*
    (void *private_data)private_data指针是file结构体中的成员之一,
    它是跨系统挑用是保存状态信息的非常有用的资源,这里我们
    在open阶段使用container_of函数得到指向结构体scull_pipe的指针并将private_data指向它
    记住要在release方法中释放其内存  
    */

    struct scull_pipe *dev = filp->private_data;

    /*使用信号量限制在同一时刻只有一个线程执行*/
    /*若被中断则返回非零值,若无法撤销任何用户可见的修改return -    EINTR*/
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    while (dev->rp == dev->wp)
    {
        up_interruptible(&dev->sem);

        /*判断用户是否设置了非堵塞IO*/
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;

        /*将线程挂起*/
        if (wait_event_interruptible(&dev->inp, dev->rp != dev->wp))
            return -ERESTARTSYS;

        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }

    /*min函数在Linux内核/include/linux/kernel.h中*/
    if (dev->wp > dev->rp)
        count = min(count, (size_t) (dev->wp - dev->rp));
    else
        count = min(count, (size_t) (dev->end - dev->rp));

    if (copy_to_user(buffer, dev->rp, count))
    {
        up_interruptible(&dev->sem);
        return -EFAULT;
    }

    dev->rp += count;
    if (dev->rp == dev->end)
        dev->rp = dev->buffer

    up_interruptible(&dev->sem);
    wake_up_interruptible(&dev->outp);
    return count;
}

/*个人仿照上方写的管道写操作代码*/
static ssize_t scull_p_write (struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos)
{
    struct scull_pipe *dev = filp->private_data;

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    /*判断条件有所不同*/
    while (strlen(dev->buffer) && dev->rp == dev->wp)
    {
        up_interruptible(&dev->sem);

        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;

        if (wait_event_interruptible(&dev->outp))
            return -ERESTARTSYS;

        down_interruptible(&dev->sem);
    }

    if (dev->wp > dev->rp)
        count = min(count, (size_t) (dev->end - dev->wp));
    else
        count = min(count, (size_t) (dev->rp - dev->wp - 1));

    if (copy_form_user(dev->wp, buffer, count))
    {
        up_interruptible(&dev->sem);
        return -EFAULT;
    }

    dev->wp += count;
    if (dev->wp == dev->end)
        dev->wp = dev->buffer;

    up_interruptible(&dev->sem);
    wake_up_interruptible(&dev->inp);
    return count;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要让字符设备驱动支持阻塞与非阻塞IO模型,需要在驱动程序中使用select/poll机制,同时使用文件操作的O_NONBLOCK标志。下面以read操作为例,简要说明如下: 1. 在设备驱动程序中,使用`file_operations`结构体中的`read`函数实现读操作,同时在该函数中添加对阻塞和非阻塞IO模型的支持。 2. 对于阻塞IO模型,可以直接在`read`函数中调用`wait_event_interruptible`函数使进程进入睡眠状态,等待数据就绪后再唤醒进程。当然在等待数据就绪的过程中,如果进程接收到了信号,则需要立即返回`-ERESTARTSYS`。 3. 对于非阻塞IO模型,需要在`read`函数中使用`O_NONBLOCK`标志进行判断。如果该标志被设置,则可以直接调用`poll_wait`函数等待数据就绪,如果数据没有准备好,则直接返回`-EAGAIN`。 4. 在`poll`函数中,需要添加对于设备文件的监控,以便在数据就绪时通知进程。这可以通过在驱动程序中添加`poll`函数来实现。在该函数中,需要使用`poll_wait`函数将当前进程添加到等待队列中,并在数据就绪时唤醒进程。 下面是一个简单的代码示例: ```c static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue); static unsigned int mydevice_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; struct mydevice_data *data = file->private_data; poll_wait(file, &read_wait_queue, wait); if (data->data_ready) { mask |= POLLIN | POLLRDNORM; } return mask; } static ssize_t mydevice_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct mydevice_data *data = file->private_data; ssize_t ret = 0; if (file->f_flags & O_NONBLOCK) { /* 非阻塞IO模型 */ if (!data->data_ready) { return -EAGAIN; } } else { /* 阻塞IO模型 */ wait_event_interruptible(read_wait_queue, data->data_ready); if (signal_pending(current)) { return -ERESTARTSYS; } } /* 读取数据 */ if (copy_to_user(buf, data->buffer, data->size)) { ret = -EFAULT; } else { ret = data->size; data->data_ready = 0; } return ret; } ``` 在上面的代码示例中,`mydevice_read`函数是`read`操作对应的函数,`mydevice_poll`函数是`poll`操作对应的函数。其中,`wait_event_interruptible`函数用于阻塞进程,等待数据就绪;`poll_wait`函数用于将进程添加到等待队列中,等待数据就绪时唤醒进程。同时,根据文件操作的O_NONBLOCK标志,判断当前使用的是阻塞IO模型还是非阻塞IO模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值