博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Nonblock I/O 及其使用

Posted on 2011-01-14 10:02  浪端之渡鸟  阅读(628)  评论(0编辑  收藏  举报

这是个很有趣的topic,而且这个东东影响到了kernel/driver和application之间的交互。

首先要来说下这个概念的东东。 当应用程序被block后,这个程序就进入了sleep状态。有可能会block的地方有:

1. open

2. read

3. write

一般情况下,文件的读写是阻塞的,可以通过fcntl()函数来设置为非阻塞读写。且这一点需要driver的配合 

好,来看看一个实际的例子的片段。

driver部分:如果该文件设置了O_NONBLOCK,则直接返回-EAGAIN。

static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
    struct scull_pipe *dev = filp->private_data;

    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

    while (dev->rp == dev->wp) { /* nothing to read */
        up(&dev->sem); /* release the lock */
        if (filp->f_flags & O_NONBLOCK)
        {
            PDEBUG("nonblock version read\n" );
            return -EAGAIN;
        }
 

        PDEBUG("\"%s\" reading: going to sleep\n", current->comm);
        if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
        /* otherwise loop, but first reacquire the lock */
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
    }
    /* ok, data is there, return something */
    if (dev->wp > dev->rp)
        count = min(count, (size_t)(dev->wp - dev->rp));
    else /* the write pointer has wrapped, return data up to dev->end */
        count = min(count, (size_t)(dev->end - dev->rp));
    if (copy_to_user(buf, dev->rp, count)) {
        up (&dev->sem);
        return -EFAULT;
    }
    dev->rp += count;
    if (dev->rp == dev->end)
        dev->rp = dev->buffer; /* wrapped */
    up (&dev->sem);

    /* finally, awake any writers and return */
    wake_up_interruptible(&dev->outq);
    PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);
    return count;
}

application部分:

使用fcntl()设置了O_NONBLOCK,这样在read的时候,就会立即返回。

而且再判断了返回值,EAGAIN表示需要再试。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>

char buffer[4096];

int main(int argc, char **argv)
{
    int delay = 1, n, m = 0;
    int fd;

    if (argc > 1)
        delay=atoi(argv[1]);
    fd = open("/dev/scullpipe0",O_RDWR );
    fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK); 

    while (1) {
        n = read(fd, buffer, 4096);
        printf("return value from read is %d\n", n);
        if (n >= 0)
            m = write(1, buffer, n); /* stdout */
        if ((n < 0 || m < 0) && (errno != EAGAIN ))
            break;
        sleep(delay);
    }
    perror(n < 0 ? "stdin" : "stdout");
    exit(1);
}