sys_read()/vfs_read()/vfs_write() Linux VFS文件系统之读写(read/write)文件

------------------------------------------------
#纯属个人理解,如有问题敬请谅解!
#kernel version: 2.6.26
#Author: andy wang
-------------------------------------------------
概述
  在上文中讨论了 VFS层是如何打开一个文件的 ,本文就来讨论 VFS读写文件的通用接口 .
 
还是根据这个图来看一下 VFS读写的流程 . VFS会根据文件描述符 fd的值在当前进程的文件描述表中找到对应的 file ,然后找到 f_op指向的索引节点 inode文件操作方法 ,最后调用 inode指向的文件读写函数完成文件的读写  ,此时 VFS层读写文件的工作就完成了 .
: VFS read的实现流程
  首先看看 read/write的软件流程图 :
从软件流程图中可以看出来, VFS处理文件的读写流程基本都是一样的.
首先按照这个流程看看read 具体是如何实现的:
[cpp]  view plain copy
  1. asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)  
  2. {  
  3.          struct file *file;  
  4.          ssize_t ret = -EBADF;  
  5.          int fput_needed;  
  6.    
  7.          file = fget_light(fd, &fput_needed);  //获取file  /*从fd中获取相应文件对象地址*/ 
  8.          if (file) {  
  9.                    loff_t pos = file_pos_read(file); //读取文件读写位置  /*获取文件访问指针*/  
  10. /*实现*/
  11.                    ret = vfs_read(file, buf, count, &pos);  //VFS 读文件  
  12.                    file_pos_write(file, pos);  //回写文件读写位置   /*位置指针移动*/
  13.                    fput_light(file, fput_needed); /*释放文件对象*/    
  14.          }  
  15.      /*返回实际读取字节数*/  
  16.          return ret;  
  17. }  

因为在我们在open文件时就把文件相关的信息放在了文件描述表的file结构中(见上文) ,所以在读一个文件前,我们需要在当前进程的文件描述表中找到这个file结构 ,那么这个file结构是如何找到的呢?
那么就来看看 fget_light()代码是如何实现的:
[cpp]  view plain copy
  1. struct file *fget_light(unsigned int fd, int *fput_needed)  
  2. {  
  3.          struct file *file;  
  4.          struct files_struct *files = current->files;  //取得当前进程的files_struct ,我们需要找到文件描述表  
  5.    
  6.          *fput_needed = 0;  
  7.          if (likely((atomic_read(&files->count) == 1))) {  
  8.                    file = fcheck_files(files, fd);   // 由fd值, 取得 file  
  9.          } else {  
  10.                    rcu_read_lock();  
  11.                    file = fcheck_files(files, fd);  
  12.                    if (file) {  
  13.                             if (atomic_inc_not_zero(&file->f_count))  
  14.                                      *fput_needed = 1;  
  15.                             else  
  16.                                      /* Didn't get the reference, someone's freed */  
  17.                                      file = NULL;  
  18.                    }  
  19.                    rcu_read_unlock();  
  20.          }  
  21.    
  22.          return file;  
  23. }  

其中关键的一个函数为fcheck_files()  ,看看代码:
[cpp]  view plain copy
  1. static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)  
  2. {  
  3.          struct file * file = NULL;  
  4.          struct fdtable *fdt = files_fdtable(files);  
  5.    
  6.          if (fd < fdt->max_fds)  
  7.                    file = rcu_dereference(fdt->fd[fd]);  
  8.          return file;  
  9. }  

函数files_fdtable(),就是去读取当前进程的文件描述表 , rcu_dereference()会根据文件描述符fd取得文件描述表中的file.
接下来获取文件读写位置file->f_pos ,这个值可以通过系统调用llseek修改.
既然我们已经在当前进程的文件描述表中找到了file , 下面就需要调用保存在file中的文件操作方法(open时初始化) .
看看下面的代码就很清楚了:
[cpp]  view plain copy
  1. ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)  
  2. {  
  3.          ssize_t ret;  
  4.      /*如果标志中不允许所请求的访问,则返回*/
  5.          if (!(file->f_mode & FMODE_READ))  //判断文件是否可读  
  6.                    return -EBADF;  
  7. /*如果没有相关的操作,则返回*/ 
  8.          if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))  //是否定义文件读方法  
  9.                    return -EINVAL;  
  10.  /*检查参数*/
  11.          if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))    
  12.                    return -EFAULT;  
  13.    
  14. /*对要访问的文件部分检查是否有冲突的强制锁*/  
  15.          ret = rw_verify_area(READ, file, pos, count);  //读校验 ,  
  16.          if (ret >= 0) {  
  17.                    count = ret;  
  18.  /*下面的方法返回实际传送的字节数,文件指针被适当的修改*/ 
  19.                    if (file->f_op->read) /*如果定义,则用他来传送数据*/     
  20.                             ret = file->f_op->read(file, buf, count, pos);  //调用文件读操作方法  
  21.                    else  
  22.  /*通用读取例程*/  
  23.                             ret = do_sync_read(file, buf, count, pos);  //通用文件模型读方法  
  24.                    if (ret > 0) {  
  25.                             fsnotify_access(file->f_path.dentry);  
  26.                             add_rchar(current, ret);  
  27.                    }  
  28.                    inc_syscr(current);  
  29.          }  
  30.      /*返回实际传送字节数*/  
  31.          return ret;  
  32. }  

上面的代码实现很简单,在做了一些条件判断以后 ,如果该文件索引节点inode定义了文件的读实现方法的话,就调用此方法. Linux下特殊文件读往往是用此方法, 一些伪文件系统如:proc,sysfs等,读写文件也是用此方法 . 而如果没有定义此方法就会调用通用文件模型的读写方法.它最终就是读内存,或者需要从存储介质中去读数据.
 
三: VFS write的实现流程
  其实VFS写文件的流程和读文件是一样的,只是调用文件操作方法不同而已 .
我们只需要看看vfs_write()的代码就可以了:
[cpp]  view plain copy
  1. ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)  
  2. {  
  3.          ssize_t ret;  
  4.    
  5.          if (!(file->f_mode & FMODE_WRITE))  
  6.                    return -EBADF;  
  7.          if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))  
  8.                    return -EINVAL;  
  9.          if (unlikely(!access_ok(VERIFY_READ, buf, count)))  
  10.                    return -EFAULT;  
  11.    
  12.          ret = rw_verify_area(WRITE, file, pos, count);  
  13.          if (ret >= 0) {  
  14.                    count = ret;  
  15.                    if (file->f_op->write)  
  16.                             ret = file->f_op->write(file, buf, count, pos);   
  17.                    else  
  18.                             ret = do_sync_write(file, buf, count, pos);  
  19.                    if (ret > 0) {  
  20.                             fsnotify_modify(file->f_path.dentry);  
  21.                             add_wchar(current, ret);  
  22.                    }  
  23.                    inc_syscw(current);  
  24.          }  
  25.    
  26.          return ret;  
  27. }  

可以看出这个函数和vfs_read()都是差不多的,只是调用的文件操作方法不同而已(file->f_op->write) ,如果没有定义file->f_op->write ,同样也需要do_sync_write()调用同样文件写操作, 首先把数据写到内存中,然后在适当的时候把数据同步到具体的存储介质中去.
 
四: 小结
    其实到这里我们已经可以深深领会到linux VFS的重要性了, 它就是为文件系统提供一个通用的接口模型,是linux文件系统的奠基石.VFS也是一个比较大的系统,要想清楚的知道其中的来龙去脉,还的要去潜心研究代码了..
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值