linux內核中socket的創建過程源碼分析(總結性質)

在漫長地分析完socket的創建源碼后,發現一片漿糊,所以特此總結,我的博客中同時有另外一篇詳細的源碼分析,內核版本為3.9,建議在閱讀本文后若還有興趣再去看另外一篇博文。絕對不要單獨看另外一篇。

一:調用鏈:

二:數據結構

一一看一下每個數據結構的意義:

1) socket, sock, inet_sock, tcp_sock的關系
創建完sk變量后,回到inet_create函數中:

這里是根據sk變量得到inet_sock變量的地址;這里注意區分各個不同結構體。
a. struct socket:這個是基本的BSD socket,面向用戶空間,應用程序通過系統調用開始創建的socket都是該結構體,它是基于虛擬文件系統創建出來的;
類型主要有三種,即流式、數據報、原始套接字協議;

b. struct sock:它是網絡層的socket;對應有TCP、UDP、RAW三種,面向內核驅動;

其狀態相比socket結構更精細:

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

g. struct tcp_sock:它是TCP協議的socket表示,是對struct inet_connection_sock的擴展,主要增加滑動窗口,擁塞控制一些TCP專用屬性;
h. struct inet_timewait_sock:它是網絡層用于超時控制的socket表示;
i. struct tcp_timewait_sock:它是TCP協議用于超時控制的socket表示;

三:具體過程

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

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


2) 入口:
net/Socket.c:sys_socketcall(),根據子系統調用號,創建socket會執行sys_socket()函數;

2、分配socket結構:
1) 調用鏈:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->sock_alloc();

2) 在socket文件系統中創建i節點:

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

上面有個條件判斷:if (sb->s_op->alloc_inode),意思是說如果當前文件系統的超級塊有自己分配inode的操作函數,則調用它自己的函數分配inode,否則從公用的高速緩存區中分配一塊inode;

3) 創建socket專用inode:
在“socket文件系統注冊”一文中后面提到,在安裝socket文件系統時,會初始化該文件系統的超級塊,此時會初始化超級塊的操作指針s_op為sockfs_ops結構;因此此時分配inode會調用sock_alloc_inode函數來完成:實際上分配了一個socket_alloc結構體,該結構體包含socket和inode,但最終返回的是該結構體中的inode成員;至此,socket結構和inode結構均分配完畢;分配inode后,應用程序便可以通過文件描述符對socket進行read()/write()之類的操作,這個是由虛擬文件系統(VFS)來完成的。

3、根據inode取得socket對象:
由于創建inode是文件系統的通用邏輯,因此其返回值是inode對象的指針;但這里在創建socket的inode后,需要根據inode得到socket對象;內聯函數SOCKET_I由此而來,這里使用兩個重要宏containerof和offsetof


4、使用協議族來初始化socket:

1) 注冊AF_INET協議域:

在“socket文件系統注冊”中提到系統初始化的工作,AF_INET的注冊也正是通過這個來完成的;

初始化入口net/ipv4/Af_inet.c:這里調用sock_register函數來完成注冊:

根據family將AF_INET協議域inet_family_ops注冊到內核中的net_families數組中;下面是其定義:

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

其中,family指定協議域的類型,create指向相應協議域的socket的創建函數;

2) 套接字類型

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

在Linux內核中,結構體struct proto表示域中的一個套接字類型,它提供該類型套接字上的所有操作及相關數據(在內核初始化時會分配相應的高速緩沖區,見上面提到的inet_init函數)。

AF_IENT域的這三種套接字類型定義用結構體inet_protosw(net/ipv4/Af_inet.c)來表示,如下:其中,tcp_prot(net/ipv4/Tcp_ipv4.c)、 udp_prot(net/ipv4/Udp.c)、raw_prot(net/ipv4/Raw.c)分別表示三種類型的套接字,分別表示相應套接字的 操作和相關數據;ops成員提供該協議域的全部操作集合,針對三種不同的套接字類型,有三種不同的域操作inet_stream_ops、 inet_dgram_ops、inet_sockraw_ops,其定義均位于net/ipv4/Af_inet.c下;

內 核初始化時,在inet_init中,會將不同的套接字存放到全局變量inetsw中統一管理;inetsw是一個鏈表數組,每一項都是一個struct inet_protosw結構體的鏈表,總共有SOCK_MAX項,在inet_init函數對AF_INET域進行初始化的時候,調用函數 inet_register_protosw把數組inetsw_array中定義的套接字類型全部注冊到inetsw數組中;其中相同套接字類型,不同 協議類型的套接字通過鏈表存放在到inetsw數組中,以套接字類型為索引,在系統實際使用的時候,只使用inetsw,而不使用 inetsw_array;

3) 使用協議域來初始化socket

了解了上面的知識后,我們再回到net/Socket.c:sys_socket()->sock_create()->__sock_create()中:

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

上面的代碼中,找到內核初始化時注冊的協議域,然后調用其create方法;

5、分配sock結構:

sk是網絡層對于socket的表示,結構體struct sock比較龐大,這里不詳細列出,只介紹一些重要的成員,sk_prot和sk_prot_creator,這兩個成員指向特定的協議處理函數集,其類型是結構體struct proto,struct proto類型的變量在協議棧中總共也有三個.其調用鏈如下:

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

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

1) 設置socket的狀態為SS_UNCONNECTED;

sock->state = SS_UNCONNECTED;

2) 根據socket的type找到對應的套接字類型:

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

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

結合源碼,sock變量的ops指向inet_stream_ops結構體變量;

4) 分配sock結構體變量 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結構體變量;

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

i) 初始化sk變量的sk_prot和sk_prot_creator;
ii) 初始化sk變量的等待隊列;
iii) 設置net空間結構,并增加引用計數;

6、建立socket結構與sock結構的關系:

inet = inet_sk(sk);

這里為什么能直接將sock結構體變量強制轉化為inet_sock結構體變量呢?只有一種可能,那就是在分配sock結構體變量時,真正分配的是inet_sock或是其他結構體;

我們回到分配sock結構體的那塊代碼(參考前面的5.4小節: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結構體時,有兩種途徑,一是從tcp專用高速緩存中分配;二是從內存直接分配;前者在初始化高速緩存時,指定了結構體大小為prot->obj_size;后者也有指定大小為prot->obj_size,

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

.obj_size = sizeof(struct tcp_sock),

也就是說,分配的真實結構體是tcp_sock;由于tcp_sock、inet_connection_sock、inet_sock、sock之間均為0處偏移量,因此可以直接將tcp_sock直接強制轉化為inet_sock。


2) 建立socket, sock的關系
創建完sock變量之后,便是初始化sock結構體,并建立sock與socket之間的引用關系;調用鏈如下:
net/Socket.c:sys_socket()->sock_create()->__sock_create()->net /ipv4/Af_inet.c:inet_create()->net/core/Sock.c:sock_init_data():
該函數主要工作是:
a. 初始化sock結構的緩沖區、隊列等;
b. 初始化sock結構的狀態為TCP_CLOSE;
c. 建立socket與sock結構的相互引用關系;


7、使用tcp協議初始化sock:
inet_create()函數最后,通過相應的協議來初始化sock結構:這里調用的是tcp_prot的init鉤子函數net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),它主要是對tcp_sock和inet_connection_sock進行一些初始化;

8、socket與文件系統關聯:

創建好與socket相關的結構后,需要與文件系統關聯,詳見sock_map_fd()函數:

1) 申請文件描述符,并分配file結構和目錄項結構;
2) 關聯socket相關的文件操作函數表和目錄項操作函數表;
3) 將file->private_date指向socket;

socket與文件系統關聯后,以后便可以通過文件系統read/write對socket進行操作了;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容