一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

基于linux 2.6.24內(nèi)核版本淺談socket的close

Linux愛好者 ? 來源:未知 ? 作者:工程師曾玲 ? 2018-08-18 11:22 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

筆者一直覺得如果能知道從應用到框架再到操作系統(tǒng)的每一處代碼,是一件Exciting的事情。上篇博客講了socket的阻塞和非阻塞,這篇就開始談一談socket的close(以tcp為例且基于linux-2.6.24內(nèi)核版本)

TCP關閉狀態(tài)轉(zhuǎn)移圖

眾所周知,TCP的close過程是四次揮手,狀態(tài)機的變遷也逃不出TCP狀態(tài)轉(zhuǎn)移圖,如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

tcp的關閉主要分主動關閉、被動關閉以及同時關閉(特殊情況,不做描述)

主動關閉

close(fd)的過程

C語言為例,在我們關閉socket的時候,會使用close(fd)函數(shù):

intsocket_fd;

socket_fd = socket(AF_INET,SOCK_STREAM,0);

...

// 此處通過文件描述符關閉對應的socket

close(socket_fd)

而close(int fd)又是通過系統(tǒng)調(diào)用sys_close來執(zhí)行的:

asmlinkage longsys_close(unsignedintfd)

{

// 清除(close_on_exec即退出進程時)的位圖標記

FD_CLR(fd,fdt->close_on_exec);

// 釋放文件描述符

// 將fdt->open_fds即打開的fd位圖中對應的位清除

// 再將fd掛入下一個可使用的fd以便復用

__put_unused_fd(files,fd);

// 調(diào)用file_pointer的close方法真正清除

retval = filp_close(filp,files);

}

我們看到最終是調(diào)用的filp_close方法:

基于linux 2.6.24內(nèi)核版本淺談socket的close

緊接著我們進入fput:

基于linux 2.6.24內(nèi)核版本淺談socket的close

同一個file(socket)有多個引用的情況很常見,例如下面的例子:

基于linux 2.6.24內(nèi)核版本淺談socket的close

所以在多進程的socket服務器編寫過程中,父進程也需要close(fd)一次,以免socket無法最終關閉

然后就是_fput函數(shù)了:

基于linux 2.6.24內(nèi)核版本淺談socket的close

由于我們討論的是socket的close,所以,我們現(xiàn)在探查下file->f_op->release在socket情況下的實現(xiàn):

f_op->release的賦值

我們跟蹤創(chuàng)建socket的代碼,即

基于linux 2.6.24內(nèi)核版本淺談socket的close

socket_file_ops的實現(xiàn)為:

staticconststructfile_operations socket_file_ops = {

.owner = THIS_MODULE,

......

// 我們在這里只考慮sock_close

.release = sock_close,

......

};

繼續(xù)跟蹤:

基于linux 2.6.24內(nèi)核版本淺談socket的close

在上一篇博客中,我們知道sock->ops為下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

即(在這里我們僅考慮tcp,即sk_prot=tcp_prot):

基于linux 2.6.24內(nèi)核版本淺談socket的close

關于fd與socket的關系如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

上圖中紅色線標注的是close(fd)的調(diào)用鏈

tcp_close

基于linux 2.6.24內(nèi)核版本淺談socket的close

四次揮手

現(xiàn)在就是我們的四次揮手環(huán)節(jié)了,其中上半段的兩次揮手下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

首先,在tcp_close_state(sk)中已經(jīng)將狀態(tài)設置為fin_wait1,并調(diào)用tcp_send_fin

voidtcp_send_fin(structsock *sk)

{

......

// 這邊設置flags為ack和fin

TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);

......

// 發(fā)送fin包,同時關閉nagle

__tcp_push_pending_frames(sk,mss_now,TCP_NAGLE_OFF);

}

如上圖Step1所示。 接著,主動關閉的這一端等待對端的ACK,如果ACK回來了,就設置TCP狀態(tài)為FIN_WAIT2,如上圖Step2所示,具體代碼如下:

基于linux 2.6.24內(nèi)核版本淺談socket的close

值的注意的是,從TCP_FIN_WAIT1變遷到TCP_FIN_WAIT2之后,還調(diào)用tcp_time_wait設置一個TCP_FIN_WAIT2定時器,在tmo+(2MSL或者基于RTO計算超時)超時后會直接變遷到closed狀態(tài)(不過此時已經(jīng)是inet_timewait_sock了)。這個超時時間可以配置,如果是ipv4的話,則可以按照下列配置:

net.ipv4.tcp_fin_timeout

/sbin/sysctl -wnet.ipv4.tcp_fin_timeout=30

如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

有這樣一步的原因是防止對端由于種種原因始終沒有發(fā)送fin,防止一直處于FIN_WAIT2狀態(tài)。

接著在FIN_WAIT2狀態(tài)等待對端的FIN,完成后面兩次揮手:

基于linux 2.6.24內(nèi)核版本淺談socket的close

由Step1和Step2將狀態(tài)置為了FIN_WAIT_2,然后接收到對端發(fā)送的FIN之后,將會將狀態(tài)設置為time_wait,如下代碼所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

time_wait狀態(tài)時,原socket會被destroy,然后新創(chuàng)建一個inet_timewait_sock,這樣就能及時的將原socket使用的資源回收。而inet_timewait_sock被掛入一個bucket中,由 inet_twdr_twcal_tick定時從bucket中將超過(2MSL或者基于RTO計算的時間)的time_wait的實例刪除。 我們來看下tcp_time_wait函數(shù)

voidtcp_time_wait(structsock *sk,intstate,inttimeo)

{

// 建立inet_timewait_sock

tw = inet_twsk_alloc(sk,state);

// 放到bucket的具體位置等待定時器刪除

inet_twsk_schedule(tw, &tcp_death_row,time,TCP_TIMEWAIT_LEN);

// 設置sk狀態(tài)為TCP_CLOSE,然后回收sk資源

tcp_done(sk);

}

具體的定時器操作函數(shù)為inet_twdr_twcal_tick,這邊就不做描述了

被動關閉

close_wait

在tcp的socket時候,如果是established狀態(tài),接收到了對端的FIN,則是被動關閉狀態(tài),會進入close_wait狀態(tài),如下圖Step1所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

具體代碼如下所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

我們再看下tcp_fin

基于linux 2.6.24內(nèi)核版本淺談socket的close

這邊有意思的點是,收到對端的fin之后并不會立即發(fā)送ack告知對端收到了,而是等有數(shù)據(jù)攜帶一塊發(fā)送,或者等攜帶重傳定時器到期后發(fā)送ack。

如果對端關閉了,應用端在read的時候得到的返回值是0,此時就應該手動調(diào)用close去關閉連接

if(recv(sockfd,buf,MAXLINE,0) == 0){

close(sockfd)

}

我們看下recv是怎么處理fin包,從而返回0的,上一篇博客可知,recv最后調(diào)用tcp_rcvmsg,由于比較復雜,我們分兩段來看:

tcp_recvmsg第一段

基于linux 2.6.24內(nèi)核版本淺談socket的close

上面代碼的處理過程如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

我們看下tcp_recmsg的第二段:

基于linux 2.6.24內(nèi)核版本淺談socket的close

由上面代碼可知,一旦當前skb讀完了而且攜帶有fin標識,則不管有沒有讀到用戶期望的字節(jié)數(shù)量都會返回已讀到的字節(jié)數(shù)。下一次再讀取的時候則在剛才描述的tcp_rcvmsg上半段直接不讀取任何數(shù)據(jù)再跳轉(zhuǎn)到found_fin_ok并返回0。這樣應用就能感知到對端已經(jīng)關閉了。 如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

last_ack

應用層在發(fā)現(xiàn)對端關閉之后已經(jīng)是close_wait狀態(tài),這時候再調(diào)用close的話,會將狀態(tài)改為last_ack狀態(tài),并發(fā)送本端的fin,如下代碼所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

在接收到主動關閉端的last_ack之后,則調(diào)用tcp_done(sk)設置sk為tcp_closed狀態(tài),并回收sk的資源,如下代碼所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

上述代碼就是被動關閉端的后兩次揮手了,如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

出現(xiàn)大量close_wait的情況

linux中出現(xiàn)大量close_wait的情況一般是應用在檢測到對端fin時沒有及時close當前連接。有一種可能如下圖所示:

基于linux 2.6.24內(nèi)核版本淺談socket的close

當出現(xiàn)這種情況,通常是minIdle之類參數(shù)的配置不對(如果連接池有定時收縮連接功能的話)。給連接池加上心跳也可以解決這種問題。

如果應用close的時間過晚,對端已經(jīng)將連接給銷毀。則應用發(fā)送給fin給對端,對端會由于找不到對應的連接而發(fā)送一個RST(Reset)報文。

操作系統(tǒng)何時回收close_wait

如果應用遲遲沒有調(diào)用close_wait,那么操作系統(tǒng)有沒有一個回收機制呢,答案是有的。 tcp本身有一個包活(keep alive)定時器,在(keep alive)定時器超時之后,會強行將此連接關閉??梢栽O置tcp keep alive的時間

/etc/sysctl.conf

net.ipv4.tcp_keepalive_intvl = 75

net.ipv4.tcp_keepalive_probes = 9

net.ipv4.tcp_keepalive_time = 7200

默認值如上面所示,設置的很大,7200s后超時,如果想快速回收close_wait可以設置小一點。但最終解決方案還是得從應用程序著手。

關于tcp keepalive包活定時器可見筆者另一篇博客:

https://my.oschina.net/alchemystar/blog/833981

進程關閉時清理socket資源

進程在退出時候(無論kill,kill -9 或是正常退出)都會關閉當前進程中所有的fd(文件描述符)

基于linux 2.6.24內(nèi)核版本淺談socket的close

這樣我們又回到了博客伊始的filp_close函數(shù),對每一個是socket的fd發(fā)送send_fin

Java GC時清理socket資源

Java的socket最終關聯(lián)到AbstractPlainSocketImpl,且其重寫了object的finalize方法

基于linux 2.6.24內(nèi)核版本淺談socket的close

所以Java會在GC時刻會關閉沒有被引用的socket,但是切記不要寄希望于Java的GC,因為GC時刻并不是以未引用的socket數(shù)量來判斷的,所以有可能泄露了一堆socket,但仍舊沒有觸發(fā)GC。

總結

linux內(nèi)核源代碼博大精深,閱讀其代碼很費周折。之前讀《TCP/IP詳解卷二》的時候由于有先輩引導和梳理,所以看書中所使用的BSD源碼并不覺得十分費勁。直到現(xiàn)在自己帶著問題獨立看linux源碼的時候,盡管有之前的基礎,仍舊被其中的各種細節(jié)所迷惑。希望筆者這篇文章能幫助到閱讀linux網(wǎng)絡協(xié)議棧代碼的人。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11508

    瀏覽量

    213551
  • Socket
    +關注

    關注

    1

    文章

    212

    瀏覽量

    35805

原文標題:從 Linux 源碼看 socket 的 close

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    如何編譯Linux內(nèi)核rpm包

    進入github官網(wǎng),搜索linux,使用git下載最新版本,或者其它版本內(nèi)核代碼。
    發(fā)表于 06-07 16:24 ?2265次閱讀
    如何編譯<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>rpm包

    linux socket 問題

    初學linux socket ,想用socket寫一個ftp,剛開始就遇到了問題,在windows下架設ftp服務器,并創(chuàng)建sail用戶,密碼111111,代碼如下,已經(jīng)能連接上ftp,也可以
    發(fā)表于 10-07 20:52

    開發(fā)板的內(nèi)核版本和源碼的內(nèi)核版本都是linux3.8,安裝模塊失敗???

    基于源碼生成的內(nèi)核版本(未下載進開發(fā)板,開發(fā)板本身運行的內(nèi)核沒有動過,我是為了編譯驅(qū)動,編譯了源碼生成了內(nèi)核)如下:開發(fā)板運行的linux3
    發(fā)表于 09-30 11:47

    WEB CLOSE_WAIT socket不釋放如何解決呢

    問題一:RTT版本3.1.5,webnet-v2.0.3,PC瀏覽器連了裝置之后,出現(xiàn)了socketCLOSE_WAIT狀態(tài),而且過了很久不釋放,后面就再也連不上WEB了,請教一下大家。問題二
    發(fā)表于 09-27 10:06

    Linux內(nèi)核教程

    本章學習目標掌握LINUX內(nèi)核版本的含義理解并掌握進程的概念掌握管道的概念及實現(xiàn)了解內(nèi)核的數(shù)據(jù)結構了解LINUX
    發(fā)表于 04-10 16:59 ?0次下載

    Linux+Socket編程

    本內(nèi)容詳細講述了Linux+Socket編程技巧指南,適合所有學習編程的廣大用戶使用
    發(fā)表于 06-10 11:19 ?0次下載
    <b class='flag-5'>Linux+Socket</b>編程

    Linux-socket網(wǎng)絡編程

    linux開發(fā)編程教程資料——Linux-socket網(wǎng)絡編程,感興趣的小伙伴們可以看一看。
    發(fā)表于 08-23 16:23 ?0次下載

    Linux 0.01版本內(nèi)核的源碼和注釋的詳細資料免費下載

    對于學習linux內(nèi)核很有幫助,能學到很多基礎性的知識。本文檔的主要內(nèi)容詳細介紹的是linux 0.01版本內(nèi)核的源碼和注釋的詳細資料免費下
    發(fā)表于 07-30 08:00 ?0次下載

    Linux內(nèi)核與Android的關系

    Android雖然建立在Linux內(nèi)核之上,但是他對內(nèi)核進行了一些擴展,增加了一些驅(qū)動。比如Binder,loger等等驅(qū)動??梢阅肁ndroid內(nèi)核代碼和其Baseline
    發(fā)表于 09-09 09:10 ?4753次閱讀

    Linux這么多的內(nèi)核版本你是怎么選的?內(nèi)核版本使用建議

    Linux Kernel 的穩(wěn)定分支維護者 Greg Kroah-Hartman 近日在其個人博客上談及了關于穩(wěn)定內(nèi)核版本的選擇。Kroah-Hartman 表示經(jīng)常會有人咨詢他們的產(chǎn)品/設備
    的頭像 發(fā)表于 10-03 12:34 ?5295次閱讀

    socket程序從linux移植到windows上

    文件描述符#define close closesocket// windows上需要額外加載和關閉socket庫#define LOAD_WIN_SOCK_LIB \WSAData wsaData
    發(fā)表于 04-02 14:41 ?491次閱讀

    谷歌Android設備內(nèi)核引入主線Linux內(nèi)核難嗎?

    Android是基于Linux內(nèi)核的操作系統(tǒng),但是,運行在Android設備上的內(nèi)核其實與Google選擇的LTS版本Linux
    的頭像 發(fā)表于 11-22 10:41 ?3309次閱讀
    谷歌Android設備<b class='flag-5'>內(nèi)核</b>引入主線<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>難嗎?

    Linux 5.4內(nèi)核正式版本有哪些新功能

    今天,Linus Torvalds正式簽署了Linux 5.4內(nèi)核的正式版本,帶來了大量新功能,強化了安全,更新了硬件驅(qū)動,你值得擁有。
    的頭像 發(fā)表于 11-25 16:31 ?1w次閱讀

    如何查看Linux系統(tǒng)版本信息

    這里所謂的Linux版本信息,包括Linux內(nèi)核版本信息和Linux系統(tǒng)
    發(fā)表于 05-19 09:11 ?3447次閱讀
    如何查看<b class='flag-5'>Linux</b>系統(tǒng)<b class='flag-5'>版本</b>信息

    Linux 6.1發(fā)布,微軟貢獻Linux內(nèi)核代碼

    此外,公告中并沒有提及 Linux 6.1 是否是 LTS 版本。按照 Linux 內(nèi)核維護者 Greg Kroah-Hartman 的說法,Lin
    的頭像 發(fā)表于 12-14 09:54 ?1520次閱讀