linux內(nèi)核中socket的創(chuàng)建過(guò)程源碼分析(總結(jié)性質(zhì))

在漫長(zhǎng)地分析完socket的創(chuàng)建源碼后,發(fā)現(xiàn)一片漿糊,所以特此總結(jié),我的博客中同時(shí)有另外一篇詳細(xì)的源碼分析,內(nèi)核版本為3.9,建議在閱讀本文后若還有興趣再去看另外一篇博文。絕對(duì)不要單獨(dú)看另外一篇。

一:調(diào)用鏈:

二:數(shù)據(jù)結(jié)構(gòu)

一一看一下每個(gè)數(shù)據(jù)結(jié)構(gòu)的意義:

1) socket, sock, inet_sock, tcp_sock的關(guān)系
創(chuàng)建完sk變量后,回到inet_create函數(shù)中:

這里是根據(jù)sk變量得到inet_sock變量的地址;這里注意區(qū)分各個(gè)不同結(jié)構(gòu)體。
a. struct socket:這個(gè)是基本的BSD socket,面向用戶空間,應(yīng)用程序通過(guò)系統(tǒng)調(diào)用開始創(chuàng)建的socket都是該結(jié)構(gòu)體,它是基于虛擬文件系統(tǒng)創(chuàng)建出來(lái)的;
類型主要有三種,即流式、數(shù)據(jù)報(bào)、原始套接字協(xié)議;

b. struct sock:它是網(wǎng)絡(luò)層的socket;對(duì)應(yīng)有TCP、UDP、RAW三種,面向內(nèi)核驅(qū)動(dòng);

其狀態(tài)相比socket結(jié)構(gòu)更精細(xì):

c. struct inet_sock:它是INET域的socket表示,是對(duì)struct sock的一個(gè)擴(kuò)展,提供INET域的一些屬性,如TTL,組播列表,IP地址,端口等;
d. struct raw_socket:它是RAW協(xié)議的一個(gè)socket表示,是對(duì)struct inet_sock的擴(kuò)展,它要處理與ICMP相關(guān)的內(nèi)容;
e. sturct udp_sock:它是UDP協(xié)議的socket表示,是對(duì)struct inet_sock的擴(kuò)展;
f. struct inet_connection_sock:它是所有面向連接的socket表示,是對(duì)struct inet_sock的擴(kuò)展;

g. struct tcp_sock:它是TCP協(xié)議的socket表示,是對(duì)struct inet_connection_sock的擴(kuò)展,主要增加滑動(dòng)窗口,擁塞控制一些TCP專用屬性;
h. struct inet_timewait_sock:它是網(wǎng)絡(luò)層用于超時(shí)控制的socket表示;
i. struct tcp_timewait_sock:它是TCP協(xié)議用于超時(shí)控制的socket表示;

三:具體過(guò)程

1、函數(shù)入口:
1) 示例代碼如下:

int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);


2) 入口:
net/Socket.c:sys_socketcall(),根據(jù)子系統(tǒng)調(diào)用號(hào),創(chuàng)建socket會(huì)執(zhí)行sys_socket()函數(shù);

2、分配socket結(jié)構(gòu):
1) 調(diào)用鏈:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->sock_alloc();

2) 在socket文件系統(tǒng)中創(chuàng)建i節(jié)點(diǎn):

inode = new_inode(sock_mnt->mnt_sb);
這里,new_inode函數(shù)是文件系統(tǒng)的通用函數(shù),其作用是在相應(yīng)的文件系統(tǒng)中創(chuàng)建一個(gè)inode;其主要代碼如下(fs/Inode.c):

上面有個(gè)條件判斷:if (sb->s_op->alloc_inode),意思是說(shuō)如果當(dāng)前文件系統(tǒng)的超級(jí)塊有自己分配inode的操作函數(shù),則調(diào)用它自己的函數(shù)分配inode,否則從公用的高速緩存區(qū)中分配一塊inode;

3) 創(chuàng)建socket專用inode:
在“socket文件系統(tǒng)注冊(cè)”一文中后面提到,在安裝socket文件系統(tǒng)時(shí),會(huì)初始化該文件系統(tǒng)的超級(jí)塊,此時(shí)會(huì)初始化超級(jí)塊的操作指針s_op為sockfs_ops結(jié)構(gòu);因此此時(shí)分配inode會(huì)調(diào)用sock_alloc_inode函數(shù)來(lái)完成:實(shí)際上分配了一個(gè)socket_alloc結(jié)構(gòu)體,該結(jié)構(gòu)體包含socket和inode,但最終返回的是該結(jié)構(gòu)體中的inode成員;至此,socket結(jié)構(gòu)和inode結(jié)構(gòu)均分配完畢;分配inode后,應(yīng)用程序便可以通過(guò)文件描述符對(duì)socket進(jìn)行read()/write()之類的操作,這個(gè)是由虛擬文件系統(tǒng)(VFS)來(lái)完成的。

3、根據(jù)inode取得socket對(duì)象:
由于創(chuàng)建inode是文件系統(tǒng)的通用邏輯,因此其返回值是inode對(duì)象的指針;但這里在創(chuàng)建socket的inode后,需要根據(jù)inode得到socket對(duì)象;內(nèi)聯(lián)函數(shù)SOCKET_I由此而來(lái),這里使用兩個(gè)重要宏containerof和offsetof


4、使用協(xié)議族來(lái)初始化socket:

1) 注冊(cè)AF_INET協(xié)議域:

在“socket文件系統(tǒng)注冊(cè)”中提到系統(tǒng)初始化的工作,AF_INET的注冊(cè)也正是通過(guò)這個(gè)來(lái)完成的;

初始化入口net/ipv4/Af_inet.c:這里調(diào)用sock_register函數(shù)來(lái)完成注冊(cè):

根據(jù)family將AF_INET協(xié)議域inet_family_ops注冊(cè)到內(nèi)核中的net_families數(shù)組中;下面是其定義:

static struct net_proto_family inet_family_ops = { .family = PF_INET, .create = inet_create, .owner = THIS_MODULE, };

其中,family指定協(xié)議域的類型,create指向相應(yīng)協(xié)議域的socket的創(chuàng)建函數(shù);

2) 套接字類型

在相同的協(xié)議域下,可能會(huì)存在多個(gè)套接字類型;如AF_INET域下存在流套接字(SOCK_STREAM),數(shù)據(jù)報(bào)套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),在這三種類型的套接字上建立的協(xié)議分別是TCP, UDP,ICMP/IGMP等。

在Linux內(nèi)核中,結(jié)構(gòu)體struct proto表示域中的一個(gè)套接字類型,它提供該類型套接字上的所有操作及相關(guān)數(shù)據(jù)(在內(nèi)核初始化時(shí)會(huì)分配相應(yīng)的高速緩沖區(qū),見上面提到的inet_init函數(shù))。

AF_IENT域的這三種套接字類型定義用結(jié)構(gòu)體inet_protosw(net/ipv4/Af_inet.c)來(lái)表示,如下:其中,tcp_prot(net/ipv4/Tcp_ipv4.c)、 udp_prot(net/ipv4/Udp.c)、raw_prot(net/ipv4/Raw.c)分別表示三種類型的套接字,分別表示相應(yīng)套接字的 操作和相關(guān)數(shù)據(jù);ops成員提供該協(xié)議域的全部操作集合,針對(duì)三種不同的套接字類型,有三種不同的域操作inet_stream_ops、 inet_dgram_ops、inet_sockraw_ops,其定義均位于net/ipv4/Af_inet.c下;

內(nèi) 核初始化時(shí),在inet_init中,會(huì)將不同的套接字存放到全局變量inetsw中統(tǒng)一管理;inetsw是一個(gè)鏈表數(shù)組,每一項(xiàng)都是一個(gè)struct inet_protosw結(jié)構(gòu)體的鏈表,總共有SOCK_MAX項(xiàng),在inet_init函數(shù)對(duì)AF_INET域進(jìn)行初始化的時(shí)候,調(diào)用函數(shù) inet_register_protosw把數(shù)組inetsw_array中定義的套接字類型全部注冊(cè)到inetsw數(shù)組中;其中相同套接字類型,不同 協(xié)議類型的套接字通過(guò)鏈表存放在到inetsw數(shù)組中,以套接字類型為索引,在系統(tǒng)實(shí)際使用的時(shí)候,只使用inetsw,而不使用 inetsw_array;

3) 使用協(xié)議域來(lái)初始化socket

了解了上面的知識(shí)后,我們?cè)倩氐絥et/Socket.c:sys_socket()->sock_create()->__sock_create()中:

pf = rcu_dereference(net_families[family]); err = pf->create(net, sock, protocol);

上面的代碼中,找到內(nèi)核初始化時(shí)注冊(cè)的協(xié)議域,然后調(diào)用其create方法;

5、分配sock結(jié)構(gòu):

sk是網(wǎng)絡(luò)層對(duì)于socket的表示,結(jié)構(gòu)體struct sock比較龐大,這里不詳細(xì)列出,只介紹一些重要的成員,sk_prot和sk_prot_creator,這兩個(gè)成員指向特定的協(xié)議處理函數(shù)集,其類型是結(jié)構(gòu)體struct proto,struct proto類型的變量在協(xié)議棧中總共也有三個(gè).其調(diào)用鏈如下:

net/Socket.c:sys_socket()->sock_create()->__sock_create()->net/ipv4/Af_inet.c:inet_create();

inet_create()主要完成以下幾個(gè)工作:

1) 設(shè)置socket的狀態(tài)為SS_UNCONNECTED;

sock->state = SS_UNCONNECTED;

2) 根據(jù)socket的type找到對(duì)應(yīng)的套接字類型:

由于同一type不同protocol的套接字保存在inetsw中的同一鏈表中,因此需要遍歷鏈表來(lái)查找;在上面的例子中,會(huì)將protocol重新賦值為answer->protocol,即IPPROTO_TCP,其值為6;

3) 使用匹配的協(xié)議族操作集初始化sk;

結(jié)合源碼,sock變量的ops指向inet_stream_ops結(jié)構(gòu)體變量;

4) 分配sock結(jié)構(gòu)體變量 net/Socket.c:sys_socket()->sock_create()->__sock_create()->net /ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sk_alloc():

其中,answer_prot指向tcp_prot結(jié)構(gòu)體變量;

其中,sk_prot_alloc分配sock結(jié)構(gòu)體變量;由于在inet_init中為不同的套接字分配了高速緩沖區(qū),因此該sock結(jié)構(gòu)體變量會(huì)在該緩沖區(qū)中分配空間;分配完成后,對(duì)其做一些初始化工作:

i) 初始化sk變量的sk_prot和sk_prot_creator;
ii) 初始化sk變量的等待隊(duì)列;
iii) 設(shè)置net空間結(jié)構(gòu),并增加引用計(jì)數(shù);

6、建立socket結(jié)構(gòu)與sock結(jié)構(gòu)的關(guān)系:

inet = inet_sk(sk);

這里為什么能直接將sock結(jié)構(gòu)體變量強(qiáng)制轉(zhuǎn)化為inet_sock結(jié)構(gòu)體變量呢?只有一種可能,那就是在分配sock結(jié)構(gòu)體變量時(shí),真正分配的是inet_sock或是其他結(jié)構(gòu)體;

我們回到分配sock結(jié)構(gòu)體的那塊代碼(參考前面的5.4小節(jié):net/core/Sock.c):

static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family) { struct sock *sk; struct kmem_cache *slab; slab = prot->slab; if (slab != NULL) sk = kmem_cache_alloc(slab, priority); else sk = kmalloc(prot->obj_size, priority); return sk; }

上面的代碼在分配sock結(jié)構(gòu)體時(shí),有兩種途徑,一是從tcp專用高速緩存中分配;二是從內(nèi)存直接分配;前者在初始化高速緩存時(shí),指定了結(jié)構(gòu)體大小為prot->obj_size;后者也有指定大小為prot->obj_size,

根據(jù)這點(diǎn),我們看下tcp_prot變量中的obj_size(net/ipv4/Tcp_ipv4.c):

.obj_size = sizeof(struct tcp_sock),

也就是說(shuō),分配的真實(shí)結(jié)構(gòu)體是tcp_sock;由于tcp_sock、inet_connection_sock、inet_sock、sock之間均為0處偏移量,因此可以直接將tcp_sock直接強(qiáng)制轉(zhuǎn)化為inet_sock。


2) 建立socket, sock的關(guān)系
創(chuàng)建完sock變量之后,便是初始化sock結(jié)構(gòu)體,并建立sock與socket之間的引用關(guān)系;調(diào)用鏈如下:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->net /ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sock_init_data():
該函數(shù)主要工作是:
a. 初始化sock結(jié)構(gòu)的緩沖區(qū)、隊(duì)列等;
b. 初始化sock結(jié)構(gòu)的狀態(tài)為TCP_CLOSE;
c. 建立socket與sock結(jié)構(gòu)的相互引用關(guān)系;


7、使用tcp協(xié)議初始化sock:
inet_create()函數(shù)最后,通過(guò)相應(yīng)的協(xié)議來(lái)初始化sock結(jié)構(gòu):這里調(diào)用的是tcp_prot的init鉤子函數(shù)net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),它主要是對(duì)tcp_sock和inet_connection_sock進(jìn)行一些初始化;

8、socket與文件系統(tǒng)關(guān)聯(lián):

創(chuàng)建好與socket相關(guān)的結(jié)構(gòu)后,需要與文件系統(tǒng)關(guān)聯(lián),詳見sock_map_fd()函數(shù):

1) 申請(qǐng)文件描述符,并分配file結(jié)構(gòu)和目錄項(xiàng)結(jié)構(gòu);
2) 關(guān)聯(lián)socket相關(guān)的文件操作函數(shù)表和目錄項(xiàng)操作函數(shù)表;
3) 將file->private_date指向socket;

socket與文件系統(tǒng)關(guān)聯(lián)后,以后便可以通過(guò)文件系統(tǒng)read/write對(duì)socket進(jìn)行操作了;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu). 關(guān)于socket的創(chuàng)建,首先需要分析socket這個(gè)結(jié)構(gòu)體,這是整個(gè)的核心。 104 str...
    ice_camel閱讀 2,954評(píng)論 1 8
  • socket通信原理 socket又被叫做套接字,它就像連接到兩端的插座孔一樣,通過(guò)建立管道,將兩個(gè)不同的進(jìn)程之間...
    jiodg45閱讀 1,228評(píng)論 0 1
  • Linux下的Socket編程(主要包括TCP部分) 轉(zhuǎn)載麻煩注明原文地址本文是Linux下基本的Socket編程...
    Bugfix閱讀 3,010評(píng)論 0 8
  • 簡(jiǎn)介 Socket理論 Socket工作流程 核心函數(shù)講解 服務(wù)的如何獲取客戶端的信息 字符串ip和網(wǎng)絡(luò)二進(jìn)制的轉(zhuǎn)...
    第八區(qū)閱讀 4,066評(píng)論 0 4
  • 當(dāng)一件事有可能發(fā)生的時(shí)候,這件事就一定會(huì)發(fā)生??僧?dāng)所有人都說(shuō)這件事會(huì)發(fā)生的時(shí)候,他便不會(huì)發(fā)生了。 所有人都說(shuō)飛機(jī)會(huì)...
    lainton閱讀 1,157評(píng)論 0 1

友情鏈接更多精彩內(nèi)容