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

简介: 作者:gfree.wind@gmail.com 博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net  本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
今天学习一下socket与VFS之间的关系。

对于socket编程,我们都知道socket也是一个文件描述符,那么socket与VFS之间究竟是如何关联的呢?

首先,创建socket的函数,socket.c的函数SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) 在成功创建了socket以后,通过函数sock_map_fd,将创建的struct socket结构映射为一个文件描述符。

  1.     /* sock为成功创建的struct socket *sock类型, 而retval为映射后的文件描述符 */
  2.     retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
  3.     if (retval 0)
  4.         goto out_release;
进入sock_map_fd
  1. int sock_map_fd(struct socket *sock, int flags)
  2. {
  3.     struct file *newfile;
  4.     /* 将sock映射为一个文件描述符fd */
  5.     int fd = sock_alloc_file(sock, &newfile, flags);

     /* 映射成功后,将fd加入到当前进程的文件描述符表中 */
  1.     if (likely(fd >= 0))
  2.         fd_install(fd, newfile);

  3.     return fd;
  4. }
进入sock_alloc_file,
  1. static int sock_alloc_file(struct socket *sock, struct file **f, int flags)
  2. {
  3.     struct qstr name = { .name = "" };
  4.     struct path path;
  5.     struct file *file;
  6.     int fd;
      
     /* 
     该函数名字稍微有点晦涩。看上去像是获得fd_flags,但是实际上是获得fd。
     我个人觉得名字叫做get_unused_fd_with_flags更好一些,这样还是突出是获得fd
     */
  1.     fd = get_unused_fd_flags(flags);
  2.     if (unlikely(fd 0))
  3.         return fd;

     /* 
     申请新的dentry,用socket对应的inode——该inode实际上是与socket同时申请下来的,参见 struct socket_      alloc结构
     初始化该dentry。
     sock_mnt为一个全局变量,为sockfs的文件系统挂载点。
     */
  1.     path.dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);
  2.     if (unlikely(!path.dentry)) {
  3.         put_unused_fd(fd);
  4.         return -ENOMEM;
  5.     }
  6.     path.mnt = mntget(sock_mnt);
    
     /* 
     将sockfs的dentry操作函数,和文件操作函数分别绑定到dentry和inode上。
     这样即完成VFS的统一调用。
     */
  1.     path.dentry->d_op = &sockfs_dentry_operations;
  2.     d_instantiate(path.dentry, SOCK_INODE(sock));
  3.     SOCK_INODE(sock)->i_fop = &socket_file_ops;
      
     /* 申请file,并将前面的dentry path与file关联起来 */
  1.     file = alloc_file(&path, FMODE_READ | FMODE_WRITE,
  2.          &socket_file_ops);
  3.     if (unlikely(!file)) {
  4.         /* drop dentry, keep inode */
  5.         atomic_inc(&path.dentry->d_inode->i_count);
  6.         path_put(&path);
  7.         put_unused_fd(fd);
  8.         return -ENFILE;
  9.     }

  10.     sock->file = file;
  11.     file->f_flags = O_RDWR | (flags & O_NONBLOCK);
  12.     file->f_pos = 0;
  13.     file->private_data = sock;

  14.     *f = file;
  15.     return fd;
  16. }
进入 get_unused_fd_flags->alloc_fd
  1. */
  2. int alloc_fd(unsigned start, unsigned flags)
  3. {
  4.     struct files_struct *files = current->files;
  5.     unsigned int fd;
  6.     int error;
  7.     struct fdtable *fdt;
     
  1.     spin_lock(&files->file_lock);
  2. repeat:
  3.     /* 得到该进程的文件描述符表 */
  4.     fdt = files_fdtable(files);
  5.     /* 从start开始查找 */
  6.     fd = start;
  7.     /* 
  8.     files->next_fd为上一次查找确定的下一个可用空闲的文件描述符。
  9.     那么这次可以直接使用next_fd
  10.     */
  11.     if (fd files->next_fd)
  12.         fd = files->next_fd;

     /* 当fd小于目前进程支持的最大的描述符号,那么可以通过fds_bits位图,从fd位开始查找,
     找到下一个0位,即下一个空闲描述符。
     */
  1.     if (fd fdt->max_fds)
  2.         fd = find_next_zero_bit(fdt->open_fds->fds_bits,
  3.                      fdt->max_fds, fd);
  1.     /* 如需要则扩展文件描述符表 */
  2.     error = expand_files(files, fd);
  3.     if (error 0)
  4.         goto out;

  5.     /*
  6.      * If we needed to expand the fs array we
  7.      * might have blocked - try again.
  8.      */
  9.     if (error)
  10.         goto repeat;
     
     /* 
     设置next_fd,用于下次加速查找空闲的fd。
     当start大于next_fd时,不会设置next_fd以避免文件描述符的不连续
     */
  1.     if (start = files->next_fd)
  2.         files->next_fd = fd + 1;

     /* 将fd添加到已打开的文件描述符表中 */
  1.     FD_SET(fd, fdt->open_fds);
  2.     if (flags & O_CLOEXEC)
  3.         FD_SET(fd, fdt->close_on_exec);
  4.     else
  5.         FD_CLR(fd, fdt->close_on_exec);
  6.     error = fd;
  7. #if 1
  8.     /* Sanity check */
  9.     if (rcu_dereference_raw(fdt->fd[fd]) != NULL) {
  10.         printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
  11.         rcu_assign_pointer(fdt->fd[fd], NULL);
  12.     }
  13. #endif

  14. out:
  15.     spin_unlock(&files->file_lock);
  16.     return error;
  17. }
今天是socket如何挂载到VFS的流程,还剩下一小部分这个流程的代码。下一次会将剩下的代码学习完毕,以及如何从VFS到socket流程。



目录
相关文章
|
3月前
|
网络协议 安全 网络安全
网络编程:基于socket的TCP/IP通信。
网络编程:基于socket的TCP/IP通信。
233 0
|
5月前
|
网络协议 安全 Java
Java网络编程入门涉及TCP/IP协议理解与Socket通信。
【6月更文挑战第21天】Java网络编程入门涉及TCP/IP协议理解与Socket通信。TCP/IP协议包括应用层、传输层、网络层和数据链路层。使用Java的`ServerSocket`和`Socket`类,服务器监听端口,接受客户端连接,而客户端连接指定服务器并交换数据。基础示例展示如何创建服务器和发送消息。进阶可涉及多线程、NIO和安全传输。学习这些基础知识能助你构建网络应用。
49 1
|
5月前
|
开发框架 网络协议 Unix
【嵌入式软件工程师面经】Socket,TCP,HTTP之间的区别
【嵌入式软件工程师面经】Socket,TCP,HTTP之间的区别
62 1
|
1月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
107 1
|
6月前
|
网络协议 Java
Java的Socket编程:TCP/IP与UDP深入探索
Java的Socket编程:TCP/IP与UDP深入探索
103 0
|
3月前
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【9月更文挑战第6天】
|
3月前
|
网络协议
socket编程(2) -- TCP通信
socket编程(2) -- TCP通信
43 0
|
4月前
|
网络协议 程序员 定位技术
学习网络的第一步:全面解析OSI与TCP/IP模型
**网络基础知识概览:** 探索网络通信的关键模型——OSI七层模型和TCP/IP五层模型。OSI模型(物理、数据链路、网络、传输、会话、表示、应用层)提供理论框架,而TCP/IP模型(物理、数据链路、网络、传输、应用层)更为实际,合并了会话、表示和应用层。两者帮助理解数据在网络中的传输过程,为网络设计和管理提供理论支持。了解这些模型,如同在复杂的网络世界中持有了地图。
97 2
|
4月前
|
网络协议 Java
如何在Java中使用Socket编程实现TCP连接?
在Java中,通过Socket编程实现TCP连接非常常见。以下演示了基本的TCP通信流程,可根据具体需求进行扩展。
263 0