Linux I/O模型深入分析

    应用程序利用read,write系统调用对设备进行读写操作时,由于设备在实际的操作中响应速度各不相同,因此数据并不总是在任何时候都可用:对读操作来说,请求的数据还没有到达设备缓冲区,对于写操作,应用传递过来的数据也许不能一下子全部放进设备狭小的缓冲区,此时内核要么返回一个错误码给上层,要么让发起读写操作的进程进入等待状态。

1,struct file_operations:Linux设备驱动的编写,基本上是围绕如何实现该结构体的函数指针成员,应用程序的read(),write()等,最终被转接到该结构体中对应的函数指针的具体实现上。

    struct file_operations {
         struct module *owner;
         loff_t (*llseek) (struct file *, loff_t, int);
         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
         ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
         ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
         int (*readdir) (struct file *, void *, filldir_t);
         unsigned int (*poll) (struct file *, struct poll_table_struct *);
         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
         int (*mmap) (struct file *, struct vm_area_struct *);
         int (*open) (struct inode *, struct file *);
         int (*flush) (struct file *);
         int (*release) (struct inode *, struct file *);
         int (*fsync) (struct file *, struct dentry *, int datasync);
         int (*aio_fsync) (struct kiocb *, int datasync);
         int (*fasync) (int, struct file *, int);
         int (*lock) (struct file *, int, struct file_lock *);
         ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
         ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
         ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long,         unsigned long);
         int (*check_flags)(int);
         int (*dir_notify)(struct file *filp, unsigned long arg);
         int (*flock) (struct file *, int, struct file_lock *);
    };

1,同步阻塞IO

    驱动程序利用等待队列实现阻塞模型。

    static wait_queue_head_t demo_wq;

    init_waitqueue_head(&demo_wq);

    在阻塞IO的file_operations实现函数中:

    static ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){

        ........

        wait_event_interruptible(demo_wq, test_bit(RD_DATA_READY, &demodev_buf->state));

        ........

    }

    唤醒操作一般在中断处理程序中:

    irqreturn_t demo_irq(int irq, void *dev_id){

        set_bit(RD_DATA_READY, &demodev_buf->state)

        wake_up_interruptible(demo_wq);

    }

2,同步非阻塞IO

     应用程序在open设备文件时指定非阻塞(O_NONBLOCK)标志,或在read之前通过fcntl设置O_NONBLOCK标志,无论哪种情况,驱动程序通过struct file *filp获得这一信息,filp->f_flags & O_NONBLOCK为真,驱动程序检查这个标志,如果设置了,设备不能立即完成用户程序所要求的IO操作,返回一个错误码(EAGAIN或EWOULDBLOCK),否则以默认阻塞方式运行。

3,异步阻塞IO

    支持这种模式的是file_operations的poll方法,用户空间通过select、poll、epoll,最终调用到驱动程序只有一个poll函数。这种模式不是阻塞在某个设备本身的读写,而是在当所有设备都不可读写时,阻塞上select、poll、epoll函数的内核实现层,当其中的某些描述符上代码的设备对读写操作就绪时,阻塞状态被解除。

    static DECLARE_WAIT_QUEUE_HEAD(demo_wq);

    unsigned int demo_poll(struct file *filp, struct poll_table_struct *wait)

    {

        struct demo_buf_list *list = filp->private_data;

        ........

        unsigned int mask = 0;

        poll_wait(filp, &demo_wq, wait)

        if(可读) mask |= POLLIN | POLLRDNORM

        return mask;

    }

    irqreturn_t demo_irq(int irq, void *dev_id){

        //数据可读唤醒进程

        wake_up_interruptible(demo_wq);

    }

4,异步非阻塞IO

    读写操作会立即返回,用户程序的读写请求将被放入一个请求队列中,由设备在后台异步完成,当设备完成了本次的读写操作时,通过信号或回调函数的方式通知用户程序。Linux设备中,只有块设备和网络设备的IO模型属于异步非阻塞,其他字符设备不需要实现。驱动需要实现file_operations对象的aio_read和aio_write方法。用户空间使用异步IO有两种方式:一是使用POSIX库函数aio_read,aio_wirte,aio_error,二是使用Linux的系统调用io_setup,io_submit,io_destory。

猜你喜欢

转载自leilianjie.iteye.com/blog/2322462
今日推荐