上一篇文章對Linux sockfs文件系統的注冊和掛載進行了分析,本文在上文基礎上進一步全面分析socket底層的相關實現。
一、socket與inode
socket在Linux中對應的文件系統叫Sockfs,每創建一個socket,就在sockfs中創建了一個特殊的文件,同時創建了sockfs文件系統中的inode,該inode唯一標識當前socket的通信。
如下圖所示,左側窗口使用nc工具創建一個TCP連接;右側找到該進程id(3384),通過查看該進程下的描述符,可以看到"3 ->socket:[86851]",socket表示這是一個socket類型的fd,[86851]表示這個一個inode號,能夠唯一標識當前的這個socket通信連接,進一步在該inode下查看"grep -i "86851" /proc/net/tcp”可以看到該TCP連接的所有信息(連接狀態、IP地址等),只不過是16進制顯示。
在分析socket與inode之前,先通過ext4文件系統舉例:
在VFS層,即抽象層,所有的文件系統都使用struct inode結構體描述indoe,然而分配inode的方式都不同,如ext4文件系統的分配inode函數是ext4_alloc_inode,如下所示:
從函數中可以看出來,函數其實是調用kmem_cache_alloc分配了 ext4_inode_info結構體(結構體如下所示),然后進行了一系列的初始化,最后返回的卻是struct inode結構體(如上面代碼的return &ei->vfs_inode)。如下結構體ext4_inode_info(ei)所示,vfs_inode是其struct inode結構體成員。
再看一下:ext4_inode、ext4_inode_info、inode之間的關聯,
ext4_inode如下所示,是磁盤上inode的結構
ext4_inode_info是ext4文件系統的inode在內存中管理結構體:
inode是文件系統抽象層:
三者的關系如下圖,struct inode是VFS抽象層的表示,ext4_inode_info是ext4文件系統inode在內存中的表示,struct ext4_inode是文件系統inode在磁盤中的表示。
VFS采用C語言的方式實現了struct inode和struct ext4_inode_info繼承關系,inode與ext4_inode_info是父類與子類的關系,并且Linux內核實現了inode與ext4_inode_info父子類的互相轉換,如下EXT4_I所示:
以上是以ext4為例進行了分析,下面將開始從socket與inode進行分析:
sockfs是虛擬文件系統,所以在磁盤上不存在inode的表示,在內核中有struct socket_alloc來表示內存中sockfs文件系統inode的相關結構體:
struct socket與struct inode的關系如下圖,正如ext4文件系統中struct ext4_inode_info與struct inode的關系類似,inode和socket_alloc結構體是父類與子類的關系。
從上面分析ext4文件系統分配inode時,是通過ext4_alloc_inode函數分配了ext4_inode_info結構體,并初始化結構體成員,函數最后返回的是ext4_inode_info中的struct inode成員。sockfs文件系統也類似,sockfs文件系統分配inode時,創建的是socket_alloc結構體,在函數最后返回的是struct inode。
從上篇文章中,分析了sockfs文件系統注冊與掛載,初始化了超級塊的函數操作集,如下所示alloc_inode是分配inode結構體的回調函數接口。
sockfs文件系統的inode分配函數是sock_alloc_inode,如下所示:
sock_alloc_inode函數分配了socket_alloc結構體,也就意味著分配了struct socket和struct inode,并最終返回了socket_alloc結構體成員inode。
故struct socket這個字段出生的時候其實就和一個struct inode結構體伴生出來的,它們倆共同封裝在struct socket_alloc中,由sockfs的sock_alloc_inode函數分配的,函數返回的是struct inode結構體.和ext4文件系統類型類似。sockfs文件系統也實現了struct inode與struct socket的轉換:
二、socket的創建與初始化
首先看一下struct socket在內核中的定義:
在內核中還有struct sock結構體,在struct socket中可以看到那么它們的關系是什么?
1、socket面向上層,sock面向下層的具體協議
2、socket是內核抽象出的一個通用結構體,主要是設置了一些跟fs相關的字段,而真正跟網絡通信相關的字段結構體是struct sock
3、struct sock是套接字的核心,是對底層具體協議做的一層抽象封裝,比如TCP協議,struct sock結構體中的成員sk_prot會賦值為tcp_prot,UDP協議會賦值為udp_prot。
(關于更多struct sock的分析將在以后的文章中分析)
創建socket的系統調用:在用戶空間創建了一個socket后,返回值是一個文件描述符。在SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)最后調用sock_map_fd進行關聯,其中返回的就是用戶空間獲取的文件描述符fd,sock就是調用sock_create創建成功的socket.
socket的創建將調用sock_create函數:
__sock_create函數調用sock_alloc函數分配socket結構和文件節點:
socket結構體的創建在sock_alloc()函數中:
new_inode_pseudo中通過繼續調用sockfs文件系統中的sock_alloc_inode函數完成struct socket_alloc的創建并返回其結構體成員struct inode。
然后調用SOCKT_I函數返回對應的struct socket。
在_sock_create中:pf->create(net, sock, protocol, kern);
通過相應的協議族,進一步調用不同的socket創建函數。pf是struct net_proto_family結構體,如下所示:
net_families[]數組里存放的是各個協議族的信息,以family字段作為下標,對應的值為net_pro_family結構體。此處我們針對TCP協議分析,因此我們family字段是AF_INET,pf->create將調用inet_create函數繼續完成底層struct sock等創建和初始化。
inet_create函數完成struct socket、struct inode、struct sock的創建與初始化后,調用sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));完成socket與文件系統的關聯,負責分配文件,并與socket進行綁定:
1、調用sock_alloc_file,分配一個struct file,并將私有數據指針指向socket結構
2、fd_install 對應文件描述符和file
get_unused_fd_flags(flags)繼續調用alloc_fd完成文件描述符的分配。
sock_alloc_file(sock, flags, NULL)分配一個struct file結構體
其中file = alloc_file(&path, FMODE_READ | FMODE_WRITE,
&socket_file_ops);分配了file結構體并進行初始化:
其中file->f_op = fop,將socket_file_ops傳遞給文件操作表
以上操作完成了struct socket、struct sock、struct file等的創建、初始化、關聯,并最終返回socket描述符fd
socket描述符fd和我們平時操作文件的文件描述符相同,那么會有一個疑問,可以看到struct file_operations socket_file_ops函數表中并沒有提供write()和read()接口,只是看到read_iter,write_iter等接口,那么系統是如何處理的呢?
以write()為例:
sys_write()->__vfs_write()
從__vfs_write函數中可以看出來,如果socket函數表中沒有提供write接口函數,則調用new_sync_write:
call_write_iter:
從以上__vfs_write()分析,如果文件函數表結構提供了write接口函數則調用write函數,如果文件函數表結構沒有提供write接口函數(如socket操作函數表中沒有提供write接口),則調用write_iter接口,即調用socket操作函數表中的sock_write_iter。就這樣通過socket fd進行普通文件系統那樣通過描述符進行讀寫等。
用戶得到socket fd,可以進行地址綁定、發送以及接收數據等操作,在Linux內核中有相關的函數完成從socket fd到struct socket、struct file的轉換:
fdget()函數從當前進程的files_struct結構中找到網絡文件系統中的file文件指針,并封裝在struct fd結構體中。sock_from函數通過得到的file結構體得到對應的socket結構指針。sock_from函數如下:
至此,socket底層來龍去脈的大體結構大概就分析到這,最為核心的struct sock相關的聯系以及底層協議的初始化等將在以后的文章進行分析。
評論
查看更多