文件数据IO 的细节分析

文件数据IO 的细节分析:

参考:
Linux设备驱动开发详解:基于最新的Linux4.0内核:宋宝华编著.

编程离不开数据, 所以数据获取是编程的第一步.


1.文件打开方式


数据IO可以是阻塞式式的或非阻塞式的.  
例如读取数据,当数据没有准备好时,是把读取线程或进程阻塞,还是立即返回一个无数据的错误. 这可由用户选择.
默认的文件打开方式是阻塞式读写. 如果想非阻塞式读写,需要如此打开文件:
int fd = open(path,O_RDONLY | O_NONBLOCK)  
这个path 可能是个设备文件,例如:/dev/globalfifo  
这个open操作将会调用到驱动中的globalfifo_open(struct inode *inode, struct file *filp);
打开的属性(O_RDONLY | O_NONBLOCK) 会保留在filp->f_flags中
filp 是系统提供的一个文件结构指针, 打开操作只需要把你的私有数据挂到filp->private_data
    filp->private_data = globalfifo_devp;
inode 对应一个文件节点,这里没有用到,先不管它.

2.用户的read操作


例如:
read(fd,buf,5);
将会调用到驱动的read 函数
ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
传来的参数包括那个特定的filp 指针, 从这可以拿到源数据信息.
还有用户空间的地址和大小. 向这个地方copy
最后1个参数ppos, 是位置偏移指针, 从这里(*ppos)=offset 可以拿到地址偏移.
所以当用户读走了count 个数据,为了保证下次继续读新数据,你需要把偏移更正一下.
        *ppos += count;
 

       
3.用户的seek操作        


seek 函数能够直接改变文件的读写指针, 他是如何使用的?
off_t lseek(int fd, off_t offset, int whence);
对应于驱动的

static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig)
这个偏移值offset, 会被保存到filp->pos 处
从开始处偏移 filp->f_pos = (unsigned int)offset; 或者从现在处偏移
filp->f_pos += offset;
这样用户读取时能被正确的传递偏移

4.再谈read 操作


驱动中的读函数,当数据不可读时,如果是O_NONBLOCK 模式,应立即返回

   while (dev->current_len == 0) {
        if (filp->f_flags & O_NONBLOCK) {
            ret = -EAGAIN;
            goto out;
        }
    }



否则需要挂起执行的进程,当资源可用时再唤醒.
挂起很简单,调用
        __set_current_state(TASK_INTERRUPTIBLE); //也允许信号唤醒
        schedule();
即可.
如何唤醒?  这需要把当前进程加到一个等待队列中
    DECLARE_WAITQUEUE(wait, current);
    add_wait_queue(&dev->r_wait, &wait);
当有数据写入时,唤醒这个等待队列
    wake_up_interruptible(&dev->r_wait);

 

5. 支持poll 操作的驱动 (select 模型,及epoll 模型)


我们都知道著名的select 函数
    select(fd + 1, &rfds, &wfds, NULL, NULL);
    它能同时监视读,写描述符集, 有一个有效即被唤醒. 它是如何实现的?
原来select 函数会调用驱动的poll 函数,每一个监控的fd的poll都会调用.

static unsigned int globalfifo_poll(struct file *filp, poll_table * wait)
它传来一个poll_table* wait, 应该有进程的信息.

    poll_wait(filp, &dev->r_wait, wait);
    poll_wait(filp, &dev->w_wait, wait);
驱动把这个wait 加入到等待队列中, 以后如果资源可用时,会唤醒这个等待队列,从而
select 能够从阻塞中继续执行.
驱动的poll函数并不会阻塞,它加入等待队列后,返回自己的状态就可以了.
    if (dev->current_len != 0) {
        mask |= POLLIN | POLLRDNORM;
    }

    if (dev->current_len != GLOBALFIFO_SIZE) {
        mask |= POLLOUT | POLLWRNORM;
    }
    
    return mask;
    这会置位fdset 的对应bit, 使用户知道哪个fd 置位了.

6. 异步IO 模型


 

   int fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR); //打开一个文件
    signal(SIGIO, signalio_handler);    //注册一个SIGIO 信号处理器
    fcntl(fd, F_SETOWN, getpid());        // 设置本fd为异步IO, 第一步,设置current pid
    int oflags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, oflags | FASYNC); // 设置为异步标记
    while (1) {
        sleep(100);
    }


    当然,要支持异步操作,驱动也要做相应修改. 主要是当数据可读时,从驱动向调用者发SIGIO信号

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值