TCP/IP源码学习(51)——socket与VFS的write关联

890阅读 0评论2013-11-08 491357604
分类:LINUX

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
在前面的博文中,学习了创建socket时,该socket与VFS的关联。今天看看在创建socket成功以后,应用层拿到的是一个socket文件描述符。而后应用层如何利用该socket文件描述符,通过VFS层进行write操作。

文件fs/read_write.c
  1. SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
  2.         size_t, count)
  3. {
  4.     struct file *file;
  5.     ssize_t ret = -EBADF;
  6.     int fput_needed;
      
     /* 
     利用描述符fd,从进程task的文件表得到kernel维护的file结构。
     参数fput_needed表示在得到file后,是否需要进行fput。
     */
  1.     file = fget_light(fd, &fput_needed);
  2.     if (file) {
  3.         //得到文件当前的偏移。对于socket来说,没有偏移的概念
  4.         loff_t pos = file_pos_read(file);
  5.         //进行写操作,并得到新的偏移量
  6.         ret = vfs_write(file, buf, count, &pos);
  7.         //更新file的偏移量
  8.         file_pos_write(file, pos);
  9.         //与fget_light对称的put操作,其中只有在fput_needed为true的情况下,才会真正执行put操作
  10.         fput_light(file, fput_needed);
  11.     }

  12.     return ret;
  13. }
进入fget_light
  1. struct file *fget_light(unsigned int fd, int *fput_needed)
  2. {
  3.     struct file *file;
  4.     struct files_struct *files = current->files;

  5.     *fput_needed = 0;
  6.     /* 
  7.     当文件表的引用为1时,表明只有该task使用该文件表,那么可以直接从文件表中获得文件描述符对应的file结构     而不为1的情况下,说明为多个task共用了该文件表,那么在访问该文件表时需要使用RCU保护
  8.     */
  9.     if (atomic_read(&files->count) == 1) {
  10.         /* 从文件描述符fd得到file结构 */
  11.         file = fcheck_files(files, fd);
  12.         /* 如果file为目录,那么肯定不可以write,重置file */
  13.         if (file && (file->f_mode & FMODE_PATH))
  14.             file = NULL;
  15.     } else {
  16.         rcu_read_lock();
  17.         file = fcheck_files(files, fd);
  18.         if (file) {
  19.             /* 
  20.             同样要检测file是否为目录,并且要在file->count不为0的情况下增加file的引用计数。
  21.             如果file->count为0,那么说明file结构已经被别人释放了。那么也要重置file.
  22.             */
  23.             if (!(file->f_mode & FMODE_PATH) &&
  24.              atomic_long_inc_not_zero(&file->f_count))
  25.                 *fput_needed = 1;
  26.             else
  27.                 /* Didn't get the reference, someone's freed */
  28.                 file = NULL;
  29.         }
  30.         rcu_read_unlock();
  31.     }

  32.     return file;
  33. }
进入vfs_write
  1. ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
  2. {
  3.     ssize_t ret;

     /* 做各种权限检查 */
  1.     if (!(file->f_mode & FMODE_WRITE))
  2.         return -EBADF;
  3.     if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
  4.         return -EINVAL;
  5.     if (unlikely(!access_ok(VERIFY_READ, buf, count)))
  6.         return -EFAULT;

  7.     ret = rw_verify_area(WRITE, file, pos, count);
  8.     if (ret >= 0) {
  9.         count = ret;
  10.         /*
  11.         在前面的文件《socket与VFS的关联1》中的socket_alloc_file函数中,已经将socket的文件操作集赋
  12.         给了socket对应的file结构。因此这里的file->f_op即为socket_file_ops。
  13.         而socket_file_ops->write为空,则下面会进入do_sync_write,即同步IO操作
  14.         */
  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);
  21.             add_wchar(current, ret);
  22.         }
  23.         inc_syscw(current);
  24.     }

  25.     return ret;
  26. }
进入do_sync_write
  1. ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
  2. {
  3.     struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
  4.     struct kiocb kiocb;
  5.     ssize_t ret;

  6.     init_sync_kiocb(&kiocb, filp);
  7.     kiocb.ki_pos = *ppos;
  8.     kiocb.ki_left = len;
  9.     kiocb.ki_nbytes = len;

  10.     for (;;) {
  11.         /* 
  12.         这里即调用socket_file_ops->aio_write,即sock_aio_write。
  13.         也就进入socket层的操作。到此完成了应用层-》内核-》VFS-》socket的过程。
  14.         */
  15.         ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
  16.         if (ret != -EIOCBRETRY)
  17.             break;
  18.         wait_on_retry_sync_kiocb(&kiocb);
  19.     }

  20.     if (-EIOCBQUEUED == ret)
  21.         ret = wait_on_sync_kiocb(&kiocb);
  22.     *ppos = kiocb.ki_pos;
  23.     return ret;
  24. }

本文只是简单描述了linux如何将应用层的写操作,通过VFS传递到socket层。

上一篇:TCP/IP源码学习(55)——skb接收层次间的关键结构
下一篇:netfilter源码学习(7)——connection track处理(3)