關於內核以及vfs對於讀寫操作的學習二

上一篇通俗講解了linux內核,vfs進行的操作,這一篇我們看看別人的源碼分析

------------------------------------------------

#純屬個人理解,如有問題敬請諒解!
#kernel version: 2.6.26
#Author: andy wang
-------------------------------------------------
概述
,本文就來討論VFS讀寫文件的通用接口. 
還是根據這個圖來看一下VFS讀寫的流程. VFS會根據文件描述符fd的值在當前進程的文件描述表中找到對應的file ,然後找到f_op指向的索引節點inode文件操作方法最後調用inode指向的文件讀寫函數完成文件的讀寫 ,此時VFS層讀寫文件的工作就完成了.
: VFS read的實現流程
  首先看看read/write的軟件流程圖:

從軟件流程圖中可以看出來, VFS處理文件的讀寫流程基本都是一樣的.
首先按照這個流程看看read 具體是如何實現的:
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
         struct file *file;
         ssize_t ret = -EBADF;
         int fput_needed;
 
         file = fget_light(fd, &fput_needed);  //獲取file
         if (file) {
                   loff_t pos = file_pos_read(file); //讀取文件讀寫位置
                   ret = vfs_read(file, buf, count, &pos);  //VFS 讀文件
                   file_pos_write(file, pos);  //回寫文件讀寫位置
                   fput_light(file, fput_needed);
         }
 
         return ret;
}
因爲在我們在open文件時就把文件相關的信息放在了文件描述表的file結構中(見上文) ,所以在讀一個文件前,我們需要在當前進程的文件描述表中找到這個file結構 ,那麼這個file結構是如何找到的呢?
那麼就來看看 fget_light()代碼是如何實現的:
struct file *fget_light(unsigned int fd, int *fput_needed)
{
         struct file *file;
         struct files_struct *files = current->files;  //取得當前進程的files_struct ,我們需要找到文件描述表
 
         *fput_needed = 0;
         if (likely((atomic_read(&files->count) == 1))) {
                   file = fcheck_files(files, fd);   // 由fd值, 取得 file
         } else {
                   rcu_read_lock();
                   file = fcheck_files(files, fd);
                   if (file) {
                            if (atomic_inc_not_zero(&file->f_count))
                                     *fput_needed = 1;
                            else
                                     /* Didn't get the reference, someone's freed */
                                     file = NULL;
                   }
                   rcu_read_unlock();
         }
 
         return file;
}
其中關鍵的一個函數爲fcheck_files()  ,看看代碼:
static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
{
         struct file * file = NULL;
         struct fdtable *fdt = files_fdtable(files);
 
         if (fd < fdt->max_fds)
                   file = rcu_dereference(fdt->fd[fd]);
         return file;
}
函數files_fdtable(),就是去讀取當前進程的文件描述表 , rcu_dereference()會根據文件描述符fd取得文件描述表中的file.
接下來獲取文件讀寫位置file->f_pos ,這個值可以通過系統調用llseek修改.
既然我們已經在當前進程的文件描述表中找到了file , 下面就需要調用保存在file中的文件操作方法(open時初始化) .
看看下面的代碼就很清楚了:
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
         ssize_t ret;
 
         if (!(file->f_mode & FMODE_READ))  //判斷文件是否可讀
                   return -EBADF;
         if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))  //是否定義文件讀方法
                   return -EINVAL;
         if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) 
                   return -EFAULT;
 
         ret = rw_verify_area(READ, file, pos, count);  //讀校驗 ,
         if (ret >= 0) {
                   count = ret;
                   if (file->f_op->read) 
                            ret = file->f_op->read(file, buf, count, pos);  //調用文件讀操作方法
                   else
                            ret = do_sync_read(file, buf, count, pos);  //通用文件模型讀方法
                   if (ret > 0) {
                            fsnotify_access(file->f_path.dentry);
                            add_rchar(current, ret);
                   }
                   inc_syscr(current);
         }
 
         return ret;
}
上面的代碼實現很簡單,在做了一些條件判斷以後 ,如果該文件索引節點inode定義了文件的讀實現方法的話,就調用此方法. Linux下特殊文件讀往往是用此方法, 一些僞文件系統如:proc,sysfs等,讀寫文件也是用此方法 . 而如果沒有定義此方法就會調用通用文件模型的讀寫方法.它最終就是讀內存,或者需要從存儲介質中去讀數據.
 
三: VFS write的實現流程
  其實VFS寫文件的流程和讀文件是一樣的,只是調用文件操作方法不同而已 .
我們只需要看看vfs_write()的代碼就可以了:
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
         ssize_t ret;
 
         if (!(file->f_mode & FMODE_WRITE))
                   return -EBADF;
         if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
                   return -EINVAL;
         if (unlikely(!access_ok(VERIFY_READ, buf, count)))
                   return -EFAULT;
 
         ret = rw_verify_area(WRITE, file, pos, count);
         if (ret >= 0) {
                   count = ret;
                   if (file->f_op->write)
                            ret = file->f_op->write(file, buf, count, pos); 
                   else
                            ret = do_sync_write(file, buf, count, pos);
                   if (ret > 0) {
                            fsnotify_modify(file->f_path.dentry);
                            add_wchar(current, ret);
                   }
                   inc_syscw(current);
         }
 
         return ret;
}
可以看出這個函數和vfs_read()都是差不多的,只是調用的文件操作方法不同而已(file->f_op->write) ,如果沒有定義file->f_op->write ,同樣也需要do_sync_write()調用同樣文件寫操作, 首先把數據寫到內存中,然後在適當的時候把數據同步到具體的存儲介質中去.

內容來自:http://blog.chinaunix.net/uid/15141543/cid-120510-list-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章