1 /* 2 * Data management: read and write 3 */ 4 5 static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, 6 loff_t *f_pos) 7 { 8 struct scull_pipe *dev = filp->private_data; 9 10 if (down_interruptible(&dev->sem)) 11 return -ERESTARTSYS; 12 13 while (dev->rp == dev->wp) { /* nothing to read */ 14 up(&dev->sem); /* release the lock */ 15 if (filp->f_flags & O_NONBLOCK) 16 return -EAGAIN; 17 PDEBUG(""%s" reading: going to sleep ", current->comm); 18 if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) 19 return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ 20 /* otherwise loop, but first reacquire the lock */ 21 if (down_interruptible(&dev->sem)) 22 return -ERESTARTSYS; 23 } 24 /* ok, data is there, return something */ 25 if (dev->wp > dev->rp) 26 count = min(count, (size_t)(dev->wp - dev->rp)); 27 else /* the write pointer has wrapped, return data up to dev->end */ 28 count = min(count, (size_t)(dev->end - dev->rp)); 29 if (copy_to_user(buf, dev->rp, count)) { 30 up (&dev->sem); 31 return -EFAULT; 32 } 33 dev->rp += count; 34 if (dev->rp == dev->end) 35 dev->rp = dev->buffer; /* wrapped */ 36 up (&dev->sem); 37 38 /* finally, awake any writers and return */ 39 wake_up_interruptible(&dev->outq); 40 PDEBUG(""%s" did read %li bytes ",current->comm, (long)count); 41 return count; 42 }
while循环在拥有设备信号量时测试缓冲区,所以函数进入之后立即down_interruptible来获取信号量。如果其中有数据,则就不进入while循环,如果其中没有数据,那么进入循环。然后释放信号量,这是因为随后要休眠必须要释放信号量。释放信号量之后,要快速检查用户请求是否是非阻塞IO,如果是那就立刻返回,否则wait_event_interruptible休眠。
当wait_event_interruptible返回那就说明有人已经唤醒我们了,有可能是进程收到一个信号,如果这个没有被阻塞的信号到达了,那就返回-ERESTARTSYS让内核的上层处理这个事件,通常由VFS内部使用,VFS或者重启系统调用,或者返回给用户空间-EINTR。
但是,就算不是信号唤醒的,我们还是无法保证是否有数据可以获得。其他进程可能也在等待数据,而且很可能赢得竞争并拿走了数据。所以我们重新获取了信号量,并让while再次检查是否有数据可读。
如果写指针回绕,那么只读到end就可以了。