zoukankan      html  css  js  c++  java
  • 设备驱动基础学习--阻塞和非阻塞读写

    一个驱动当它无法立刻满足请求应当如何响应? 一个对 read 的调用可能当没有数据时到来, 而以后会期待更多的数据. 或者一个进程可能试图写, 但是你的设备没有准备好接受数据, 因为你的输出缓冲满了. 调用进程往往不关心这种问题; 程序员只希望调用 read 或 write 并且使调用返回, 在必要的工作已完成后. 这样, 在这样的情形中, 你的驱动应当(缺省地)阻塞进程, 使它进入睡眠直到请求可继续。

    有时还有调用进程通知你他不想阻塞, 不管它的 I/O 是否继续. 明确的非阻塞 I/O 由filp->f_flags 中的 O_NONBLOCK 标志来指示. 这个标志定义于 <linux/fcntl.h>, 被 <linux/fs.h>自动包含。如果指定 O_NONBLOCK, read 和 write 的行为是不同的. 在这个情况下, 这个调用简单地返回 -EAGAIN(("try it agin")如果一个进程当没有数据可用时调用 read , 或者如果当缓冲中没有空间时它调用 write . 如你可能期望的, 非阻塞操作立刻返回, 允许这个应用程序轮询数据.

    fellowmisc.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/types.h>
    #include <linux/miscdevice.h>
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    #include <linux/wait.h>
    #include <linux/semaphore.h>
    #include <linux/sched.h>
    #include "fellowmisc.h"
    #define BUFF_SIZE 16
    struct fellowmisc_dev{
      struct miscdevice misc;
      struct miscdata data;
      char *buffer;//环形buffer
      int wp;
      int rp;
      int buffer_size;
      int free;
      wait_queue_head_t inq,outq;
      struct semaphore sem;
    };
    struct fellowmisc_dev *fellowmisc_devp;
    int fellowmisc_open(struct inode *inode, struct file *filep)
    {
      filep->private_data = fellowmisc_devp;
      return 0;
    }
    int fellowmisc_release(struct inode *inode, struct file *filep)
    {
      return 0;
    }
    ssize_t fellowmisc_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)
    {
      size_t read_count = 0;
      struct fellowmisc_dev *devp = (struct fellowmisc_dev*)filep->private_data;
      char *tmpbuffer = kzalloc(count, GFP_KERNEL);
      if (down_interruptible(&(devp->sem)))
        return -ERESTARTSYS;
      while (devp->free == devp->buffer_size)//当buffer是空的时,没数据可读
      {
        up(&(devp->sem));
        if (filep->f_flags & O_NONBLOCK)//如果是非阻塞,返回EAGAIN
          return -EAGAIN;
        printk("%s reading: going to sleep ", current->comm);
        if (wait_event_interruptible(devp->inq, (devp->free != devp->buffer_size)))//如果是阻塞,则将进程放到等待队列睡眠,等到有数据写唤醒。
          return -ERESTARTSYS;
        if (down_interruptible(&(devp->sem)))
          return -ERESTARTSYS;
       }
      read_count = count < (devp->buffer_size - devp->free) ? count : (devp->buffer_size - devp->free);

      if (devp->wp > devp->rp)
        strncpy(tmpbuffer, devp->buffer + devp->rp, read_count);
      else
      {
        strncpy(tmpbuffer, devp->buffer + devp->rp, devp->buffer_size - devp->rp);
        strncpy(tmpbuffer + devp->buffer_size - devp->rp, devp->buffer, read_count - (devp->buffer_size - devp->rp));
      }
      if (copy_to_user(buf, tmpbuffer,read_count))
      {
        up(&(devp->sem));
        return -EFAULT;
      }
      devp->rp += read_count;
      if (devp->rp >= devp->buffer_size)
      devp->rp -= devp->buffer_size;
      devp->free += read_count;
      up(&(devp->sem));
      wake_up_interruptible(&(devp->outq));
      printk("%s read %d bytes ", current->comm, read_count);
      kfree(tmpbuffer);
      return read_count;
    }

    ssize_t fellowmisc_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)
    {
      size_t write_count;
      struct fellowmisc_dev *devp = (struct fellowmisc_dev*)filep->private_data;
      char *tmpbuffer = kzalloc(count, GFP_KERNEL);
      if (down_interruptible(&(devp->sem)))
        return -ERESTARTSYS;
      while (0 == devp->free)//如果buffer满了,则无法写入。
      {
        up(&(devp->sem));
        if (filep->f_flags & O_NONBLOCK)
          return -EAGAIN;
        printk("%s writing: going to sleep ", current->comm);
        if (wait_event_interruptible(devp->outq, (0 != devp->free)))//如果是阻塞,则将进程放到等待队列睡眠,等到buffer有空间唤醒。
          return -ERESTARTSYS;
        if (down_interruptible(&(devp->sem)))
          return -ERESTARTSYS;
      }
      write_count = count < devp->free ? count : devp->free;
      if (copy_from_user(tmpbuffer, buf, count))
      {
        up(&(devp->sem));
        return -EFAULT;
      }
      if (devp->wp + write_count < devp->buffer_size)
        strncpy(devp->buffer + devp->wp, tmpbuffer, write_count);
      else
      {
        strncpy(devp->buffer + devp->wp, tmpbuffer, devp->buffer_size - devp->wp);
        strncpy(devp->buffer, tmpbuffer + devp->buffer_size - devp->wp, write_count - (devp->buffer_size - devp->wp));
      }
      devp->wp += write_count;
      if (devp->wp >= devp->buffer_size)
      devp->wp -= devp->buffer_size;
      devp->free -= write_count;
      up(&(devp->sem));
      wake_up_interruptible(&(devp->inq));
      printk("%s write %d bytes ", current->comm, write_count);
      kfree(tmpbuffer);
      return write_count;
    }

    long fellowmisc_ioctl(struct file *filep,unsigned int cmd,unsigned long arg)

    {

      int ret = 0;
      struct fellowmisc_dev *devp = (struct fellowmisc_dev *)(filep->private_data);
      if (_IOC_TYPE(cmd) != FELLOW_MISC_IOC_MAGIC)
        return -EINVAL;
      if (_IOC_NR(cmd) > FELLOW_MISC_IOC_MAXNR)
        return -EINVAL;
      switch(cmd)
      {
        case FELLOW_MISC_IOC_PRINT:
          printk("FELLOW_MISC_IOC_PRINT ");
          printk("val:%d, size: %d, str: %s ", devp->data.val, devp->data.size, devp->data.str);
        break;
        case FELLOW_MISC_IOC_SET:
          printk("FELLOW_MISC_IOC_SET ");
          ret = copy_from_user((unsigned char*)&(devp->data), (unsigned char *)arg, sizeof(struct miscdata));
          printk("set val:%d, size: %d, str: %s ", devp->data.val, devp->data.size, devp->data.str);
        break;
        case FELLOW_MISC_IOC_GET:
          printk("FELLOW_MISC_IOC_GET ");
          ret = copy_to_user((unsigned char*)arg,(unsigned char*)&(devp->data), sizeof(struct miscdata));
        break;
        default:
          return -EINVAL;

      }
      return ret;
    }

    static const struct file_operations fellowmisc_fops ={
    .owner = THIS_MODULE,
    .open = fellowmisc_open,
    .release = fellowmisc_release,
    .unlocked_ioctl = fellowmisc_ioctl,
    .read = fellowmisc_read,
    .write = fellowmisc_write,
    };
    static struct miscdevice fellow_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "fellowmisc",
    .fops = &fellowmisc_fops,
    };
    static int fellowmisc_init(void)
    {
      int ret = 0;
      printk("fellowmisc_init ");
      fellowmisc_devp = kmalloc(sizeof(struct fellowmisc_dev), GFP_KERNEL);
      if (!fellowmisc_devp)
      {
        ret = -ENOMEM;
        goto fail;
      }
      memset(&(fellowmisc_devp->data), 0, sizeof(fellowmisc_devp->data));
      fellowmisc_devp->misc = fellow_misc;
      init_waitqueue_head(&(fellowmisc_devp->inq));
      init_waitqueue_head(&(fellowmisc_devp->outq));
      fellowmisc_devp->buffer = kzalloc(BUFF_SIZE, GFP_KERNEL);
      fellowmisc_devp->wp = fellowmisc_devp->rp = 0;
      fellowmisc_devp->buffer_size = fellowmisc_devp->free = BUFF_SIZE;

      sema_init(&(fellowmisc_devp->sem), 1);
      return misc_register(&(fellowmisc_devp->misc));
    fail:
      return ret;
    }

    static void fellowmisc_exit(void)
    {
      misc_deregister(&(fellowmisc_devp->misc));
      kfree(fellowmisc_devp->buffer);
      kfree(fellowmisc_devp);

    }

    MODULE_AUTHOR("fellow");
    MODULE_LICENSE("GPL");
    module_init(fellowmisc_init);
    module_exit(fellowmisc_exit);

    app.c

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <string.h>
    #include <pthread.h>
    #include "fellowmisc.h"
    void *read_thread(void *arg)
    {
      int fd = *(int *)arg;
      char readdata[8];
      int ret = 0;
      printf("read thread! ");
      int n = 0;
      while (n++ < 5)
      {
        memset(readdata, 0, sizeof(readdata));
        if ((ret = read(fd, readdata, sizeof(readdata)))< 0)
        {
          printf("read fail:%s ", strerror(errno));
        }
        else
        {
          printf("readdata:%s, ret= %d ", readdata, ret);
        }
        sleep(1);
      }
      return NULL;
    }
    void *write_thread(void *arg)
    {
      int fd = *(int *)arg;
      char writedata[8] = "abcdefg";
      int ret = 0;
      printf("write thread! ");
      int n = 0;
      while (n++ <5)
      {
        if ((ret = write(fd, writedata, sizeof(writedata)))< 0)
        {
          printf("write fail:%s ", strerror(errno));
        }
        else
        {
          printf("writedata:%s, ret: %d ", writedata, ret);
        }
        sleep(1);
      }
    return NULL;
    }
    int main(int argc, char **argv)
    {
      int fd = -1;
      fd = open("/dev/fellowmisc", O_RDWR);
      if (fd < 0)
      {
        printf("open fail:%s ", strerror(errno));
        return -1;
      }
      if (argc == 2 && !strncmp(argv[1], "block", strlen("block")))
      {
        printf("open with block");
      }
      else
      {
        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
      }
      pthread_t tid_read;
      pthread_t tid_write;
      pthread_create(&tid_read, NULL, read_thread, &fd);
      pthread_create(&tid_write, NULL, write_thread, &fd);
      sleep(10);

    close(fd);
    return 0;
    }

  • 相关阅读:
    String&StringBuffer&StringBuilder区别
    linux启动流程简介
    nginx视频直播/点播服务干货分享
    Http协议的认识
    php中对象是引用类型吗?
    nginx与php-fpm 504 Gateway Time-out 排查与解决案例
    php header函数常见用途
    php魔术方法
    ajax 和jsonp 不是一码事 细读详解
    php session redis 配置
  • 原文地址:https://www.cnblogs.com/fellow1988/p/6271071.html
Copyright © 2011-2022 走看看