Chinaunix首页 | 论坛 | 博客
  • 博客访问: 336015
  • 博文数量: 92
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 960
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-21 19:38
文章分类

全部博文(92)

文章存档

2010年(71)

2009年(21)

我的朋友

分类: 嵌入式

2010-04-11 20:27:37

Linux设备驱动中的阻塞I/O

·         如果一个进程调用 read 但是没有数据可用, 这个进程必须阻塞. 这个进程在有数据达到时被立刻唤醒, 并且那个数据被返回给调用者, 即便小于在给方法的 count 参数中请求的数量.

·         如果一个进程调用 write 并且在缓冲中没有空间, 这个进程必须阻塞, 并且它必须在一个与用作 read 的不同的等待队列中. 当一些数据被写入硬件设备, 并且在输出缓冲中的空间变空闲, 这个进程被唤醒并且写调用成功, 尽管数据可能只被部分写入如果在缓冲只没有空间给被请求的 count 字节.

我将用具体的代码分析来掌握Linux阻塞的写法

先梳理一下简要的流程:

1.    wait_queue_t wait;      //;定义一个等待队列
2.    init_wait_queue_entry(&wait, current);    //;
初始化
3.    add_wait_queue(&my_queue, &wait);    //;
把我们定义的等待队列项加入到这个等待队列中
4.    current->state = TASK_INTERRUPTILBE;    //;
设置为休眠状态,将要进入睡眠
5.    schedule();       //;
真正进入睡眠
6.    remove_wait_queue(&my_queue, &wait);    //;
事件到达,schedule()返回

/*======================================================================

    A globalfifo driver as an example of char device drivers 

    This example is to introduce poll,blocking and non-blocking access

     

    The initial developer of the original code is Baohua Song

    . All Rights Reserved.

======================================================================*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define GLOBALFIFO_SIZE     0x1000    /*全局fifo最大4K字节*/

#define FIFO_CLEAR 0x1  /*0全局内存的长度*/

#define GLOBALFIFO_MAJOR 253    /*预设的globalfifo的主设备号*/

 

static int globalfifo_major = GLOBALFIFO_MAJOR;

/*globalfifo设备结构体*/

struct globalfifo_dev                                    

{                                                        

  struct cdev cdev; /*cdev结构体*/                      

  unsigned int current_len;    /*fifo有效数据长度*/

  unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/       

  struct semaphore sem; /*并发控制用的信号量*/          

  wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/    

  wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/    

};

 

struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/

/*文件打开函数*/

int globalfifo_open(struct inode *inode, struct file *filp)

{

  /*将设备结构体指针赋值给文件私有数据指针*/

  filp->private_data = globalfifo_devp;

  return 0;

}

/*文件释放函数*/

int globalfifo_release(struct inode *inode, struct file *filp)

{

  return 0;

}

 

/* ioctl设备控制函数 */

static int globalfifo_ioctl(struct inode *inodep, struct file *filp, unsigned

  int cmd, unsigned long arg)

{

  struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/

 

  switch (cmd)

  {

    case FIFO_CLEAR:

           down(&dev->sem); //获得信号量        

      dev->current_len = 0;

      memset(dev->mem,0,GLOBALFIFO_SIZE);

      up(&dev->sem); //释放信号量

        

      printk(KERN_INFO "globalfifo is set to zero\n");     

      break;

 

    default:

      return  - EINVAL;

  }

  return 0;

}

 

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)

{

  unsigned int mask = 0;

  struct globalfifo_dev *dev = filp->private_data; /*获得设备结构体指针*/

 

  down(&dev->sem);

 

  poll_wait(filp, &dev->r_wait, wait);

  poll_wait(filp, &dev->w_wait, wait); 

  /*fifo非空*/

  if (dev->current_len != 0)

  {

    mask |= POLLIN | POLLRDNORM; /*标示数据可获得*/

  }

  /*fifo非满*/

  if (dev->current_len != GLOBALFIFO_SIZE)

  {

    mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/

  }

    

  up(&dev->sem);

  return mask;

}

 

 

/*globalfifo读函数*/

static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,

  loff_t *ppos)

{

  int ret;

  struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针

  DECLARE_WAITQUEUE(wait, current); //定义等待队列

 

  down(&dev->sem); //获得信号量,标记使用该资源

  add_wait_queue(&dev->r_wait, &wait); //进入读等待队列头

 

  /* 等待FIFO非空 */

  while (dev->current_len == 0)

  {

    if (filp->f_flags &O_NONBLOCK)

    {

      ret =  - EAGAIN;

      goto out;

    }

    __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠

    up(&dev->sem);

 

    schedule(); //调度其他进程执行

if (signal_pending(current)) //如果是因为信号唤醒,也是schedule()返回的地方

    {

      ret =  - ERESTARTSYS;

      goto out2;

    }

 

    down(&dev->sem);

  }

 

  /* 拷贝到用户空间 */

  if (count > dev->current_len)

    count = dev->current_len;

 

/*copy_to_user()返回的是未被执行复制的字节数,因为有读的需求,所以要唤起写*/

  if (copy_to_user(buf, dev->mem, count))

  {

    ret =  - EFAULT;

    goto out;

  }

  else

  {

    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifo数据前移

    dev->current_len -= count; //有效数据长度减少

    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);

    

/*读的需求被满足了,内存被腾出来了,也顾唤醒了写等待队列*/

    wake_up_interruptible(&dev->w_wait);

   

    ret = count;

  }

  out: up(&dev->sem); //释放信号量

 

/*将写等待移出,也就是发了一个写信号,跳转到写的schedule()下一个函数*/

  out2:remove_wait_queue(&dev->w_wait, &wait);

  set_current_state(TASK_RUNNING);

  return ret;

}

 

 

/*globalfifo写操作*/

static ssize_t globalfifo_write(struct file *filp, const char __user *buf,

  size_t count, loff_t *ppos)

{

  struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针

  int ret;

  DECLARE_WAITQUEUE(wait, current); //定义等待队列

 

  down(&dev->sem); //获取信号量

  add_wait_queue(&dev->w_wait, &wait); //进入写等待队列头

 

  /* 等待FIFO非满 */

  while (dev->current_len == GLOBALFIFO_SIZE)

  {

    if (filp->f_flags &O_NONBLOCK)

    //如果是非阻塞访问

    {

      ret =  - EAGAIN;

      goto out;

    }

    __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠

    up(&dev->sem);

 

    schedule(); //调度其他进程执行

    if (signal_pending(current))

    //如果是因为信号唤醒

    {

      ret =  - ERESTARTSYS;

      goto out2;

    }

 

    down(&dev->sem); //获得信号量

  }

 

  /*从用户空间拷贝到内核空间*/

  if (count > GLOBALFIFO_SIZE - dev->current_len)

    count = GLOBALFIFO_SIZE - dev->current_len;

 

  if (copy_from_user(dev->mem + dev->current_len, buf, count))

  {

    ret =  - EFAULT;

    goto out;

  }

  else

  {

    dev->current_len += count;

    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev

      ->current_len);

 

    wake_up_interruptible(&dev->r_wait); //唤醒读等待队列

   

    ret = count;

  }

 

  out: up(&dev->sem); //释放信号量

  out2:remove_wait_queue(&dev->w_wait, &wait); //从附属的等待队列头移除

  set_current_state(TASK_RUNNING);

  return ret;

}

 

阅读(1006) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~