Linux內(nèi)核 |socket底層的來龍去脈
轉(zhuǎn)載自技術(shù)簡(jiǎn)說
上一篇文章淺析Linux sockfs文件系統(tǒng)對(duì)Linux sockfs文件系統(tǒng)的注冊(cè)和掛載進(jìn)行了分析,本文在上文基礎(chǔ)上進(jìn)一步全面分析socket底層的相關(guān)實(shí)現(xiàn)。
一、socket與inode
socket在Linux中對(duì)應(yīng)的文件系統(tǒng)叫Sockfs,每創(chuàng)建一個(gè)socket,就在sockfs中創(chuàng)建了一個(gè)特殊的文件,同時(shí)創(chuàng)建了sockfs文件系統(tǒng)中的inode,該inode唯一標(biāo)識(shí)當(dāng)前socket的通信。
如下圖所示,左側(cè)窗口使用nc工具創(chuàng)建一個(gè)TCP連接;右側(cè)找到該進(jìn)程id(3384),通過查看該進(jìn)程下的描述符,可以看到“3 ->socket:[86851]”,socket表示這是一個(gè)socket類型的fd,[86851]表示這個(gè)一個(gè)inode號(hào),能夠唯一標(biāo)識(shí)當(dāng)前的這個(gè)socket通信連接,進(jìn)一步在該inode下查看“grep -i ”86851“ /proc/net/tcp”可以看到該TCP連接的所有信息(連接狀態(tài)、IP地址等),只不過是16進(jìn)制顯示。

在分析socket與inode之前,先通過ext4文件系統(tǒng)舉例:
在VFS層,即抽象層,所有的文件系統(tǒng)都使用struct inode結(jié)構(gòu)體描述indoe,然而分配inode的方式都不同,如ext4文件系統(tǒng)的分配inode函數(shù)是ext4_alloc_inode,如下所示:
從函數(shù)中可以看出來,函數(shù)其實(shí)是調(diào)用kmem_cache_alloc分配了 ext4_inode_info結(jié)構(gòu)體(結(jié)構(gòu)體如下所示),然后進(jìn)行了一系列的初始化,最后返回的卻是struct inode結(jié)構(gòu)體(如上面代碼的return &ei->vfs_inode)。如下結(jié)構(gòu)體ext4_inode_info(ei)所示,vfs_inode是其結(jié)構(gòu)體成員。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!?。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ? ?


再看一下:ext4_inode、ext4_inode_info、inode之間的關(guān)聯(lián),
ext4_inode如下所示,是磁盤上諾德的結(jié)構(gòu)
ext4_inode_info是ext4文件系統(tǒng)的inode在內(nèi)存中管理結(jié)構(gòu)體:
inode是文件系統(tǒng)抽象層:
三者的關(guān)系如下圖,struct inode是VFS抽象層的表示,ext4_inode_info是ext4文件系統(tǒng)inode在內(nèi)存中的表示,struct ext4_inode是文件系統(tǒng)inode在磁盤中的表示。

VFS采用C語言的方式實(shí)現(xiàn)了struct inode和struct ext4_inode_info繼承關(guān)系,inode與ext4_inode_info是父類與子類的關(guān)系 ,并且Linux內(nèi)核實(shí)現(xiàn)了inode與ext4_inode_info父子類的互相轉(zhuǎn)換,如下EXT4_I所示:
以上是以ext4為例進(jìn)行了分析,下面將開始從socket與inode進(jìn)行分析:
sockfs是虛擬文件系統(tǒng),所以在磁盤上不存在inode的表示,在內(nèi)核中有struct socket_alloc來表示內(nèi)存中sockfs文件系統(tǒng)inode的相關(guān)結(jié)構(gòu)體:
struct socket與struct inode的關(guān)系如下圖,正如ext4文件系統(tǒng)中struct ext4_inode_info與struct inode的關(guān)系類似,inode和socket_alloc結(jié)構(gòu)體是父類與子類的關(guān)系。

從上面分析ext4文件系統(tǒng)分配inode時(shí),是通過ext4_alloc_inode函數(shù)分配了ext4_inode_info結(jié)構(gòu)體,并初始化結(jié)構(gòu)體成員,函數(shù)最后返回的是ext4_inode_info中的struct inode成員。sockfs文件系統(tǒng)也類似,sockfs文件系統(tǒng)分配inode時(shí),創(chuàng)建的是socket_alloc結(jié)構(gòu)體,在函數(shù)最后返回的是struct inode。
從上一篇文章淺析Linux sockfs文件系統(tǒng)中,分析了sockfs文件系統(tǒng)注冊(cè)與掛載,初始化了超級(jí)塊的函數(shù)操作集,如下所示alloc_inode是分配inode結(jié)構(gòu)體的回調(diào)函數(shù)接口。
sockfs文件系統(tǒng)的inode分配函數(shù)是sock_alloc_inode,如下所示:
sock_alloc_inode函數(shù)分配了socket_alloc結(jié)構(gòu)體,也就意味著分配了struct socket和struct inode,并最終返回了socket_alloc結(jié)構(gòu)體成員inode。
故struct socket這個(gè)字段出生的時(shí)候其實(shí)就和一個(gè)struct inode結(jié)構(gòu)體伴生出來的,它們倆共同封裝在struct socket_alloc中,由sockfs的sock_alloc_inode函數(shù)分配的,函數(shù)返回的是struct inode結(jié)構(gòu)體.和ext4文件系統(tǒng)類型類似。sockfs文件系統(tǒng)也實(shí)現(xiàn)了struct inode與struct socket的轉(zhuǎn)換:
二、socket的創(chuàng)建與初始化
首先看一下struct socket在內(nèi)核中的定義:
在內(nèi)核中還有struct sock結(jié)構(gòu)體,在struct socket中可以看到那么它們的關(guān)系是什么?
1、socket面向上層,sock面向下層的具體協(xié)議
2、socket是內(nèi)核抽象出的一個(gè)通用結(jié)構(gòu)體,主要是設(shè)置了一些跟fs相關(guān)的字段,而真正跟網(wǎng)絡(luò)通信相關(guān)的字段結(jié)構(gòu)體是struct sock
3、struct sock是套接字的核心,是對(duì)底層具體協(xié)議做的一層抽象封裝,比如TCP協(xié)議,struct sock結(jié)構(gòu)體中的成員sk_prot會(huì)賦值為tcp_prot,UDP協(xié)議會(huì)賦值為udp_prot。
(關(guān)于更多struct sock的分析將在以后的文章中分析)
**創(chuàng)建socket的系統(tǒng)調(diào)用:**在用戶空間創(chuàng)建了一個(gè)socket后,返回值是一個(gè)文件描述符。在SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)最后調(diào)用sock_map_fd進(jìn)行關(guān)聯(lián),其中返回的就是用戶空間獲取的文件描述符fd,sock就是調(diào)用sock_create創(chuàng)建成功的socket.
socket的創(chuàng)建將調(diào)用sock_create函數(shù):
__sock_create函數(shù)調(diào)用sock_alloc函數(shù)分配socket結(jié)構(gòu)和文件節(jié)點(diǎn):
socket結(jié)構(gòu)體的創(chuàng)建在sock_alloc()函數(shù)中:
new_inode_pseudo中通過繼續(xù)調(diào)用sockfs文件系統(tǒng)中的sock_alloc_inode函數(shù)完成struct socket_alloc的創(chuàng)建并返回其結(jié)構(gòu)體成員結(jié)構(gòu)
然后調(diào)用SOCKT_I函數(shù)返回對(duì)應(yīng)的struct socket。
在_sock_create中:pf->create(net, sock, protocol, kern);
通過相應(yīng)的協(xié)議族,進(jìn)一步調(diào)用不同的socket創(chuàng)建函數(shù)。pf是struct net_proto_family結(jié)構(gòu)體,如下所示:
net_families[]數(shù)組里存放的是各個(gè)協(xié)議族的信息,以family字段作為下標(biāo),對(duì)應(yīng)的值為net_pro_family結(jié)構(gòu)體。此處我們針對(duì)TCP協(xié)議分析,因此我們family字段是AF_INET,pf->create將調(diào)用inet_create函數(shù)繼續(xù)完成底層結(jié)構(gòu) sock等創(chuàng)建和初始化。
inet_create函數(shù)完成struct socket、struct inode、struct sock的創(chuàng)建與初始化后,調(diào)用sock_map_fd(sock, flags & (O_CLOEXEC |O_NONBLOCK));完成socket與文件系統(tǒng)的關(guān)聯(lián),負(fù)責(zé)分配文件,并與socket進(jìn)行綁定:
1、調(diào)用sock_alloc_file,分配一個(gè)struct file,并將私有數(shù)據(jù)指針指向socket結(jié)構(gòu)
2、fd_install 對(duì)應(yīng)文件描述符和文件
get_unused_fd_flags(flags)繼續(xù)調(diào)用alloc_fd完成文件描述符的分配。
sock_alloc_file(sock, flags, NULL)分配一個(gè)結(jié)構(gòu)文件結(jié)構(gòu)體
其中file = alloc_file(&path, FMODE_READ |FMODE_WRITE, &socket_file_ops);分配了file結(jié)構(gòu)體并進(jìn)行初始化:
其中file->f_op = fop,將socket_file_ops傳遞給文件操作表
以上操作完成了struct socket、struct sock、struct file等的創(chuàng)建、初始化、關(guān)聯(lián),并最終返回socket描述符fd

socket描述符fd和我們平時(shí)操作文件的文件描述符相同,那么會(huì)有一個(gè)疑問,可以看到struct file_operations socket_file_ops函數(shù)表中并沒有提供write()和read()接口,只是看到read_iter,write_iter等接口,那么系統(tǒng)是如何處理的呢?
以write()為例:
sys_write()->__vfs_write()
從__vfs_write函數(shù)中可以看出來,如果socket函數(shù)表中沒有提供write接口函數(shù),則調(diào)用new_sync_write:
call_write_iter:
從以上__vfs_write()分析,如果文件函數(shù)表結(jié)構(gòu)提供了write接口函數(shù)則調(diào)用write函數(shù),如果文件函數(shù)表結(jié)構(gòu)沒有提供write接口函數(shù)(如socket操作函數(shù)表中沒有提供write接口),則調(diào)用write_iter接口,即調(diào)用socket操作函數(shù)表中的sock_write_iter。就這樣通過socket fd進(jìn)行普通文件系統(tǒng)那樣通過描述符進(jìn)行讀寫等。
用戶得到socket fd,可以進(jìn)行地址綁定、發(fā)送以及接收數(shù)據(jù)等操作,在Linux內(nèi)核中有相關(guān)的函數(shù)完成從socket fd到struct socket、struct file的轉(zhuǎn)換:
fdget()函數(shù)從當(dāng)前進(jìn)程的files_struct結(jié)構(gòu)中找到網(wǎng)絡(luò)文件系統(tǒng)中的file文件指針,并封裝在struct fd結(jié)構(gòu)體中。sock_from函數(shù)通過得到的file結(jié)構(gòu)體得到對(duì)應(yīng)的socket結(jié)構(gòu)指針。sock_from函數(shù)如下:
至此,socket底層來龍去脈的大體結(jié)構(gòu)大概就分析到這,最為核心的struct sock相關(guān)的聯(lián)系以及底層協(xié)議的初始化等將在以后的文章進(jìn)行分析。
