学习ldd3--阻塞型 IO(第六章)

来源:互联网 发布:linux重命名文件的命令 编辑:程序博客网 时间:2024/05/19 03:20
作者:张伟AreS
/*******************************************************************************/
代码:D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\main.c
      D:\学习\个人学习笔记及网络经典文章\学习LDD3笔记\ldd3_examples\scull\pipe.c
先学习下手动休眠方式:
   创建和初始化一个等待队列:DEFINE_WAIT(my_wait);其中, name 是等待队列入口项的名
   也可分两步:wait_queue_t my_wait;  init_wait(&my_wait);
  
   添加你的等待队列入口到队列, 并且设置进程状态. 2个任务都由这个函数处理:
   void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);
queue和wait分别地是等待队列头和进程入口.state是进程的新状态;它应当或者是TASK_INTERRUPTIBLE(给可中断的睡眠,这常常是你所要的)或者TASK_UNINTERRUPTIBLE(给
不可中断睡眠).
  
   在调用 prepare_to_wait 之后, 进程可调用 schedule()进行休眠。从schedule()返回时,进程状态为TASK_RUNNING
   schedule()返回后,就需要清理:void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
阻塞进程,使用休眠
读取函数scull_p_read:
static ssize_t scull_p_read (struct file *filp, char _ _ user *buf,size_t count, loff_t *f_ops)
{
   struct scull_pipe *dev = filp->private_data;
  
   if (down_interruptible(&dev->dem))//返回0表示信号量成功,返回1表示申请失败
    return -ERESTARTYS;
   while(dev-rp = = dev->wp)//无数据可读
     {
        up(&dev->dem);//释放信号量锁
        if(filp->f_flags & O_NONBLOCK)//如果用户请求是非阻塞,则不能阻塞进程,退出。
           return -EAGAIN;
        if(wait_event_interrupible(dev->inq,(dev->rp!= dev->wp)))//阻塞读进程(即此进程),返回非0表示阻塞被中断唤醒,返回0,表示成功休眠读进程到dev->rp队列。    
           return -ERESTARTYS;
        if(down_interruptible(&dev->dem)//当进程被唤醒,重新加锁准备进行读取,但先要重新进行while循环判断唤醒条件是否为真
           return -ERESTARTYS;
     }
   /*数据准备就绪*/ 
  if(dev->wp>dev->rp)
   count = min(count,(size_t)(dev->wp-dev->rp));
  else
   count = min(count,(size_t)(dev->end-dev->rp));//dev->end指针指向此空间末端
  
   /*
   *调用copy_to_user将数据拷贝到用户空间过程中,可能会休眠。
   *此时,虽然进程拥有互斥锁,但这种情况下拥有互斥锁休眠是可以接受的,
   *因为我们知道内核会把数据复制到用户空间并唤醒我们,同时不会试图锁上同一个信号量
   */
  if(copy_to_user(buf,dev->rp,count))//返回0表示读取count个值成功,返回非零数字表示还需读取字节数。dev->buff指针指向此空间开始处
    { up(&dev->dem);
     returtn -EFAULT;
    }  
   dev->rp+=count;
   if(dev->rp==dev->end)
     dev->rp == dev->buff;
   up(&dev->dem);
  
   /*最后唤醒写进程*/
   wake_up_interruptible(&dev->outq);
   reurn count;
}
 
学习写入函数scull_p_write之前,先学习处理真正休眠的代码:
   /*使用spacefree函数判断是否有空间可写*/  
   /* How much space is free? */
static int spacefree(struct scull_pipe *dev)
{
 if (dev->rp == dev->wp)//如果没有数据可读,说明空间是空的,全部可写
  return dev->buffersize - 1;
 return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;//计算剩余空着可写入的空间
}
  
 /*手动设置休眠*/
static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
{
 while (spacefree(dev) == 0) { /* 没有空间可以写入,spacefree函数在上面定义*/
  DEFINE_WAIT(wait);//创建并初始化一个等待队列入口
  
  up(&dev->sem);
  if (filp->f_flags & O_NONBLOCK)
   return -EAGAIN;
  PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
  prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);//将等待队列入口wait添加到dev->ouq等待队列头中
  if (spacefree(dev) == 0)//检查休眠等待条件,如果不作检查,可能引入竞态
  /*
   *如果在上行的if判断和下行的schedule()调度之间,有了可写入空间,那么会发生什么情况?
   *这种情况下没有任何问题,只要进程已经把自己放到了等待队列中并修改了进程状态,就不会有任何问题,
   *schedule会把进程状态重新设置为TASK_RUNNING
    */
   schedule();//当前进程让出CPU,进入等待休眠
  finish_wait(&dev->outq, &wait);
  if (signal_pending(current))
   return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
  if (down_interruptible(&dev->sem))
   return -ERESTARTSYS;
   //注意wihle循环,被唤醒后重新判断是否可写
 }
 return 0;

  
学习写入函数scull_p_write,和读取函数类似:
static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
 struct scull_pipe *dev = filp->private_data;
 int result;
 if (down_interruptible(&dev->sem))
  return -ERESTARTSYS;
 /* Make sure there's space to write */
 result = scull_getwritespace(dev, filp);
 if (result)
  return result; /* scull_getwritespace called up(&dev->sem) */
 /* ok, space is there, accept something */
 count = min(count, (size_t)spacefree(dev));
 if (dev->wp >= dev->rp)
  count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */
 else /* the write pointer has wrapped, fill up to rp-1 */
  count = min(count, (size_t)(dev->rp - dev->wp - 1));
 PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, dev->wp, buf);
 if (copy_from_user(dev->wp, buf, count)) {
  up (&dev->sem);
  return -EFAULT;
 }
 dev->wp += count;
 if (dev->wp == dev->end)
  dev->wp = dev->buffer; /* wrapped */
 up(&dev->sem);
 /* finally, awake any reader */
 wake_up_interruptible(&dev->inq);  /* blocked in read() and select() */
 /* and signal asynchronous readers, explained late in chapter 5 */
 if (dev->async_queue)
  kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
 PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count);
 return count;
}
/******************************************************************************/
原创粉丝点击