设备驱动基础学习--阻塞和非阻塞读写

一个驱动当它无法立刻满足请求应当如何响应? 一个对 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\n", 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\n", 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\n", 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\n", 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\n");
      printk("val:%d, size: %d, str: %s\n", devp->data.val, devp->data.size, devp->data.str);
    break;
    case FELLOW_MISC_IOC_SET:
      printk("FELLOW_MISC_IOC_SET\n");
      ret = copy_from_user((unsigned char*)&(devp->data), (unsigned char *)arg, sizeof(struct miscdata));
      printk("set val:%d, size: %d, str: %s\n", devp->data.val, devp->data.size, devp->data.str);
    break;
    case FELLOW_MISC_IOC_GET:
      printk("FELLOW_MISC_IOC_GET\n");
      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\n");
  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!\n");
  int n = 0;
  while (n++ < 5)
  {
    memset(readdata, 0, sizeof(readdata));
    if ((ret = read(fd, readdata, sizeof(readdata)))< 0)
    {
      printf("read fail:%s\n", strerror(errno));
    }
    else
    {
      printf("readdata:%s, ret= %d\n", 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!\n");
  int n = 0;
  while (n++ <5)
  {
    if ((ret = write(fd, writedata, sizeof(writedata)))< 0)
    {
      printf("write fail:%s\n", strerror(errno));
    }
    else
    {
      printf("writedata:%s, ret: %d\n", 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\n", 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;
}

posted @ 2017-01-10 22:24  fellow_jing  阅读(1263)  评论(0编辑  收藏  举报