Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1217198
  • 博文数量: 404
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 5382
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-03 16:29
文章存档

2010年(40)

2009年(140)

2008年(224)

我的朋友

分类: LINUX

2008-09-27 11:35:18

终于有时间继续更新blog了。让我们来看看非常牛b的第六章到底讲了些什么吧

一 阻塞型IO的实现

休眠的两条原则

a 永远不要在原子上下文中休眠。即我们的程序不能在拥有自旋锁 seqlock,等休眠。而用于信号量时休眠是可以的。
b 对唤醒后的状态不要作任何假设,必须验证我们等待的条件。
静态定义和初始化一个等待队列头
DECLARE_WAIT_QUEUE_HEAD(name);
动态
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);

简单休眠

wait_event_interruptible(queue,condition)
wake_up_interruptible(queue)
成对使用

高级休眠

初始化等待队列入口
DEFINE_WAIT(my_wait)
或者
wait_queue_t my_wait;
init_wait (&my_wait)
将等待队列入口添加到队列中
prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state)
进入schedule
清理工作
finish_wait(wait_queue_head_t * queue, wait_queue_t * wait)

书中的例子使用两个等待队列,读的等待队列使用简单休眠的方式,而写的使用了高级休眠的方式,代码如下
 


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)
            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;
}
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;
}

/* Wait for space for writing; caller must hold device semaphore. On
 * error the semaphore will be released before returning. */

static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
{
    while (spacefree(dev) == 0) { /* full */
        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);
        if (spacefree(dev) == 0)
            schedule();
        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;

          /*继续循环*/
    }
    return 0;
}


非常的详细的代码
编译测试,开窗口a
cat /dev/cddtp
程序阻塞,直到开窗口b
ls -l > /dev/cddtp
窗口a才输出
drwxrwxrwx    3 com.acce trusted      1024 May 17 15:01 RecordedSound
drwxrwxrwx    2 root     root         1024 May 17 14:59 drm
drwxrwxrwx    9 root     root         1024 May 17 15:01 public
代码里面也实现了非阻塞的IO
 if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN
;
当没有数据可以读的时候立即返回 EAGIN.

二 poll and select

在驱动的file_operations增加一个poll函数

static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
    struct scull_pipe *dev = filp->private_data;
    unsigned int mask = 0;

    
/*
     * The buffer is circular; it is considered full
     * if "wp" is right behind "rp" and empty if the
     * two are equal.
     */

    down(&dev->sem);
    poll_wait(filp, &dev->inq, wait);
    poll_wait(filp, &dev->outq, wait);
    if (dev->rp != dev->wp)
        mask |= POLLIN | POLLRDNORM;    /* readable */
    if (spacefree(dev))
        mask |= POLLOUT | POLLWRNORM;    /* writable */
    up(&dev->sem);
    return mask;
}


void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);
poll_wait函数并不阻塞,程序中poll_wait(filp, &dev->inq, wait)这句话的意思并不是说一直等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的。select/poll会在一个循环中对每个需要监听的设备调用它们自己的poll支持函数以使得当前进程被加入各个设备的等待列表。若当前没有任何被监听的设备就绪,则内核进行调度(调用schedule)让出cpu进入阻塞状态,schedule返回时将再次循环检测是否有操作可以进行,如此反复;否则,若有任意一个设备就绪, select/poll都立即返回。

hrpLinux联盟hrpLinux联盟用户端程序需要调用select()来进行select.
hrpLinux联盟
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
hrpLinux联盟
  其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,numfds的值是需要检查的号码最高的文件描述符加1。timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。struct timeval数据结构为: hrpLinux联盟
hrpLinux联盟
struct timeval hrpLinux联盟
{ hrpLinux联盟
  int tv_sec; /* seconds */ hrpLinux联盟
  int tv_usec; /* microseconds */ hrpLinux联盟
};
hrpLinux联盟
  除此之外,我们还将使用下列API:hrpLinux联盟
hrpLinux联盟
  FD_ZERO(fd_set *set)――清除一个文件描述符集; hrpLinux联盟
  FD_SET(int fd,fd_set *set)――将一个文件描述符加入文件描述符集中; hrpLinux联盟
  FD_CLR(int fd,fd_set *set)――将一个文件描述符从文件描述符集中清除; hrpLinux联盟
  FD_ISSET(int fd,fd_set *set)――判断文件描述符是否被置位。

三 异步通知


用户程序必须执行 2 个步骤来使能来自输入文件的异步通知. 首先, 它们指定一个进程作为文件的拥有者. 当一个进程使用 fcntl 系统调用发出 F_SETOWN 命令, 这个拥有者进程的 ID 被保存在 filp->f_owner 给以后使用. 这一步对内核知道通知谁是必要的. 为了真正使能异步通知, 用户程序必须设置 FASYNC 标志在设备中, 通过 F_SETFL fcntl 命令.

在这 2 个调用已被执行后, 输入文件可请求递交一个 SIGIO 信号, 无论何时新数据到达. 信号被发送给存储于 filp->f_owner 中的进程(或者进程组, 如果值为负值).

例如, 下面的用户程序中的代码行使能了异步的通知到当前进程, 给 stdin 输入文件:

从内核的观点:
  • 1. 当发出 F_SETOWN, 什么都没发生, 除了一个值被赋值给 filp->f_owner.

  • 2. 当 F_SETFL 被执行来打开 FASYNC, 驱动的 fasync 方法被调用. 这个方法被调用无论何时 FASYNC 的值在 filp->f_flags 中被改变来通知驱动这个变化, 因此它可正确地响应. 这个标志在文件被打开时缺省地被清除. 我们将看这个驱动方法的标准实现, 在本节.

  • 3. 当数据到达, 所有的注册异步通知的进程必须被发出一个 SIGIO 信号.

驱动调用的 2 个函数对应下面的原型:

int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa, int sig, int band);
在file* 结构中含有f_owner,来标识异步通知的进程
/* remove this filp from the asynchronously notified filp's */
scull_p_fasync(-1, filp, 0);

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