+ -
当前位置:首页 → 问答吧 → Linux系统中,read文件过程分析

Linux系统中,read文件过程分析

时间:2010-08-14

来源:互联网

抽空把read文件的过程给理了一下,记录下来
如果有记录的有问题请大家多多指教,好让小弟看到自己疏漏的地方

Kernel version: 2.6.35
read一个文件
首先是通过系统调用open一个文件
然后通过系统调用去read一个文件,为什么man 2 read的时候或者man 2 write的时候的参数与写的驱动的read和write里面定义的函数看上去不同呢?
  1. ssize_t read(int fd, void *buf, size_t count);
  2. ssize_t write(int fd, const void *buf, size_t count);
复制代码
下面是driver/nvram.c里面的
  1. static ssize_t nvram_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  2. static ssize_t nvram_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
复制代码
下面就以说read为例就可以了
  1. 391 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
  2. 392 {
  3. 393     struct file *file;
  4. 394     ssize_t ret = -EBADF;
  5. 395     int fput_needed;
  6. 396
  7. 397     file = fget_light(fd, &fput_needed);
  8. 398     if (file) {
  9. 399         loff_t pos = file_pos_read(file);
  10. 400         ret = vfs_read(file, buf, count, &pos);
  11. 401         file_pos_write(file, pos);
  12. 402         fput_light(file, fput_needed);
  13. 403     }
  14. 404
  15. 405     return ret;
  16. 406 }
复制代码
通过阅读代码,发现这个系统调用read与man看到的系统调用的定义的是相同的,没有这里可以没有疑问,但是这个比nvram.有些不同,其实操作都是在这个系统调用里面,struct

file *file结构里面的file是通过这个fget_light来或得到的,这个file结构如下:
  1. 918 struct file {
  2. 919     /*
  3. 920      * fu_list becomes invalid after file_free is called and queued via
  4. 921      * fu_rcuhead for RCU freeing
  5. 922      */
  6. 923     union {
  7. 924         struct list_head    fu_list;
  8. 925         struct rcu_head     fu_rcuhead;
  9. 926     } f_u;
  10. 927     struct path     f_path;
  11. 928 #define f_dentry    f_path.dentry
  12. 929 #define f_vfsmnt    f_path.mnt
  13. 930     const struct file_operations    *f_op;
  14. 931     spinlock_t      f_lock;  /* f_ep_links, f_flags, no IRQ */
  15. 932     atomic_long_t       f_count;
  16. 933     unsigned int        f_flags;
  17. 934     fmode_t         f_mode;
  18. 935     loff_t          f_pos;
  19. 936     struct fown_struct  f_owner;
  20. 937     const struct cred   *f_cred;
  21. 938     struct file_ra_state    f_ra;
  22. 939
  23. 940     u64         f_version;
  24. 941 #ifdef CONFIG_SECURITY
  25. 942     void            *f_security;
  26. 943 #endif
  27. 944     /* needed for tty driver, and maybe others */
  28. 945     void            *private_data;
  29. 946
  30. 947 #ifdef CONFIG_EPOLL
  31. 948     /* Used by fs/eventpoll.c to link all the hooks to this file */
  32. 949     struct list_head    f_ep_links;
  33. 950 #endif /* #ifdef CONFIG_EPOLL */
  34. 951     struct address_space    *f_mapping;
  35. 952 #ifdef CONFIG_DEBUG_WRITECOUNT
  36. 953     unsigned long f_mnt_write_state;
  37. 954 #endif
  38. 955 };
复制代码
从上面可以看到f_pos,记录偏移值的,后面read的时候会用到,下面继续说ppos,其实就是这个loff_t *ppos,这个是通过file_pos_read来或得到的,
  1. 381 static inline loff_t file_pos_read(struct file *file)
  2. 382 {
  3. 383     return file->f_pos;
  4. 384 }
复制代码
这个f_pos在每一次read的时候,都有可能会改变偏移量,继续进入vfs_read去读文件:
  1. 295 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
  2. 296 {
  3. 297     ssize_t ret;
  4. 298
  5. 299     if (!(file->f_mode & FMODE_READ))
  6. 300         return -EBADF;
  7. 301     if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
  8. 302         return -EINVAL;
  9. 303     if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
  10. 304         return -EFAULT;
  11. 305
  12. 306     ret = rw_verify_area(READ, file, pos, count);
  13. 307     if (ret >= 0) {
  14. 308         count = ret;
  15. 309         if (file->f_op->read)
  16. 310             ret = file->f_op->read(file, buf, count, pos);
  17. 311         else
  18. 312             ret = do_sync_read(file, buf, count, pos);
  19. 313         if (ret > 0) {
  20. 314             fsnotify_access(file);
  21. 315             add_rchar(current, ret);
  22. 316         }
  23. 317         inc_syscr(current);
  24. 318     }
  25. 319
  26. 320     return ret;
  27. 321 }
复制代码
先确认一下要读的文件是否可以去读,如果不让读或者不让写的话,就只能直接推出去了,否则可以继续
上面的代码里面有两个read接口,一个是file的read,一个是do_sync_read,下面直接说file->f_op里面的read,这个read是在写设备驱动的时候,或者文件系统加载的时候注册的

read
下面看设备驱动部分的read
  1. 231 static ssize_t nvram_read(struct file *file, char __user *buf,
  2. 232                         size_t count, loff_t *ppos)
  3. 233 {
  4. 234     unsigned char contents[NVRAM_BYTES];
  5. 235     unsigned i = *ppos;
  6. 236     unsigned char *tmp;
  7. 237
  8. 238     spin_lock_irq(&rtc_lock);
  9. 239
  10. 240     if (!__nvram_check_checksum())
  11. 241         goto checksum_err;
  12. 242
  13. 243     for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
  14. 244         *tmp = __nvram_read_byte(i);
  15. 245
  16. 246     spin_unlock_irq(&rtc_lock);
  17. 247
  18. 248     if (copy_to_user(buf, contents, tmp - contents))
  19. 249         return -EFAULT;
  20. 250
  21. 251     *ppos = i;
  22. 252
  23. 253     return tmp - contents;
  24. 254
  25. 255 checksum_err:
  26. 256     spin_unlock_irq(&rtc_lock);
  27. 257     return -EIO;
  28. 258 }
复制代码
这里就不用多说了,ppos是有需要的,当然,有些设备驱动里面可以不用这个ppos,比如keyboard的驱动一类的只要一个值的,但是如果想获得很大的一段buffer的话,这个估计

就有必要了。
接下来说do_sync_read文件,这个就要会想一下注册文件系统时,对fops的注册了
比如ext4文件系统里面,在ext4_file_super里面有个ext4_iget
struct inode *ext4_iget(struct super_block *sb, unsigned long ino)

在这个接口里面会有注册fops的操作:
  1. 5165     if (S_ISREG(inode->i_mode)) {
  2. 5166         inode->i_op = &ext4_file_inode_operations;
  3. 5167         inode->i_fop = &ext4_file_operations;
  4. 5168         ext4_set_aops(inode);
  5. 5169     } else if (S_ISDIR(inode->i_mode)) {
  6. 5170         inode->i_op = &ext4_dir_inode_operations;
  7. 5171         inode->i_fop = &ext4_dir_operations;
  8. 5172     } else if (S_ISLNK(inode->i_mode)) {
  9. 5173         if (ext4_inode_is_fast_symlink(inode)) {
  10. 5174             inode->i_op = &ext4_fast_symlink_inode_operations;
  11. 5175             nd_terminate_link(ei->i_data, inode->i_size,
  12. 5176                 sizeof(ei->i_data) - 1);
  13. 5176                 printk(KERN_WARNING "[[email protected]]\n");
  14. 5177         } else {
  15. 5178             inode->i_op = &ext4_symlink_inode_operations;
  16. 5179             ext4_set_aops(inode);
  17. 5180         }
  18. 5181     } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
  19. 5182           S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
  20. 5183         inode->i_op = &ext4_special_inode_operations;
  21. 5184         if (raw_inode->i_block[0])
  22. 5185             init_special_inode(inode, inode->i_mode,
  23. 5186                old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));
  24. 5187         else
  25. 5188             init_special_inode(inode, inode->i_mode,
  26. 5189                new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
  27. 5190     } else {
复制代码
在这个里面可以知道了,为什么这里的是inode呢?这个在open里面应该有对应的答案,接下来继续进继续看文件操作部分
[T-bagwell]
  1. 133 const struct file_operations ext4_file_operations = {
  2. 134     .llseek     = generic_file_llseek,
  3. 135     .read       = do_sync_read,
  4. 136     .write      = do_sync_write,
  5. 137     .aio_read   = generic_file_aio_read,
  6. 138     .aio_write  = ext4_file_write,
  7. 139     .unlocked_ioctl = ext4_ioctl,
  8. 140 #ifdef CONFIG_COMPAT
  9. 141     .compat_ioctl   = ext4_compat_ioctl,
  10. 142 #endif
  11. 143     .mmap       = ext4_file_mmap,
  12. 144     .open       = ext4_file_open,
  13. 145     .release    = ext4_release_file,
  14. 146     .fsync      = ext4_sync_file,
  15. 147     .splice_read    = generic_file_splice_read,
  16. 148     .splice_write   = generic_file_splice_write,
  17. 149 };
  18. 150
复制代码
其实文件操作就是和do_sync_read是一样的操作,最终会进入到generic_file_aio_read,里面
generic_file_aio_read里面就是从快设备里面读取内容了,到这里,如文件结束

作者: T-Bagwell   发布时间: 2010-08-14

支持

作者: prolj   发布时间: 2010-08-14

写的好

作者: qinjiana0786   发布时间: 2010-08-14

支持楼主,拜读了

作者: 8516947   发布时间: 2010-08-14

多谢分享。

不过LZ分析的似乎是嵌入式的吧,ARM类的?因为我看到了NVRAM.

似乎不是传统的X86-PC + HARD DISK. 没仔细看,也许不对。

作者: accessory   发布时间: 2010-08-14