在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

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

3天內不再提示

基于linux 2.6.24內核版本淺談socket的close

Linux愛好者 ? 來源:未知 ? 作者:工程師曾玲 ? 2018-08-18 11:22 ? 次閱讀

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

TCP關閉狀態轉移圖

眾所周知,TCP的close過程是四次揮手,狀態機的變遷也逃不出TCP狀態轉移圖,如下圖所示:

基于linux 2.6.24內核版本淺談socket的close

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

主動關閉

close(fd)的過程

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

intsocket_fd;

socket_fd = socket(AF_INET,SOCK_STREAM,0);

...

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

close(socket_fd)

而close(int fd)又是通過系統調用sys_close來執行的:

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);

// 調用file_pointer的close方法真正清除

retval = filp_close(filp,files);

}

我們看到最終是調用的filp_close方法:

基于linux 2.6.24內核版本淺談socket的close

緊接著我們進入fput:

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

然后就是_fput函數了:

基于linux 2.6.24內核版本淺談socket的close

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

f_op->release的賦值

我們跟蹤創建socket的代碼,即

基于linux 2.6.24內核版本淺談socket的close

socket_file_ops的實現為:

staticconststructfile_operations socket_file_ops = {

.owner = THIS_MODULE,

......

// 我們在這里只考慮sock_close

.release = sock_close,

......

};

繼續跟蹤:

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

tcp_close

基于linux 2.6.24內核版本淺談socket的close

四次揮手

現在就是我們的四次揮手環節了,其中上半段的兩次揮手下圖所示:

基于linux 2.6.24內核版本淺談socket的close

首先,在tcp_close_state(sk)中已經將狀態設置為fin_wait1,并調用tcp_send_fin

voidtcp_send_fin(structsock *sk)

{

......

// 這邊設置flags為ack和fin

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

......

// 發送fin包,同時關閉nagle

__tcp_push_pending_frames(sk,mss_now,TCP_NAGLE_OFF);

}

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

基于linux 2.6.24內核版本淺談socket的close

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

net.ipv4.tcp_fin_timeout

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

如下圖所示:

基于linux 2.6.24內核版本淺談socket的close

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

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

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

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狀態為TCP_CLOSE,然后回收sk資源

tcp_done(sk);

}

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

被動關閉

close_wait

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

基于linux 2.6.24內核版本淺談socket的close

具體代碼如下所示:

基于linux 2.6.24內核版本淺談socket的close

我們再看下tcp_fin

基于linux 2.6.24內核版本淺談socket的close

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

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

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

close(sockfd)

}

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

tcp_recvmsg第一段

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

我們看下tcp_recmsg的第二段:

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

last_ack

應用層在發現對端關閉之后已經是close_wait狀態,這時候再調用close的話,會將狀態改為last_ack狀態,并發送本端的fin,如下代碼所示:

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

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

基于linux 2.6.24內核版本淺談socket的close

出現大量close_wait的情況

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

基于linux 2.6.24內核版本淺談socket的close

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

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

操作系統何時回收close_wait

如果應用遲遲沒有調用close_wait,那么操作系統有沒有一個回收機制呢,答案是有的。 tcp本身有一個包活(keep alive)定時器,在(keep alive)定時器超時之后,會強行將此連接關閉。可以設置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內核版本淺談socket的close

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

Java GC時清理socket資源

Java的socket最終關聯到AbstractPlainSocketImpl,且其重寫了object的finalize方法

基于linux 2.6.24內核版本淺談socket的close

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

總結

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

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

    關注

    87

    文章

    11322

    瀏覽量

    209857
  • Socket
    +關注

    關注

    0

    文章

    212

    瀏覽量

    34743

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

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

收藏 人收藏

    評論

    相關推薦

    如何編譯Linux內核rpm包

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

    linux socket 問題

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

    開發板的內核版本和源碼的內核版本都是linux3.8,安裝模塊失敗???

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

    WEB CLOSE_WAIT socket不釋放如何解決呢

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

    Linux內核教程

    本章學習目標掌握LINUX內核版本的含義理解并掌握進程的概念掌握管道的概念及實現了解內核的數據結構了解LINUX
    發表于 04-10 16:59 ?0次下載

    Linux+Socket編程

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

    Linux-socket網絡編程

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

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

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

    Linux內核與Android的關系

    Android雖然建立在Linux內核之上,但是他對內核進行了一些擴展,增加了一些驅動。比如Binder,loger等等驅動。可以拿Android內核代碼和其Baseline
    發表于 09-09 09:10 ?4601次閱讀

    Linux這么多的內核版本你是怎么選的?內核版本使用建議

    Linux Kernel 的穩定分支維護者 Greg Kroah-Hartman 近日在其個人博客上談及了關于穩定內核版本的選擇。Kroah-Hartman 表示經常會有人咨詢他們的產品/設備
    的頭像 發表于 10-03 12:34 ?4953次閱讀

    socket程序從linux移植到windows上

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

    谷歌Android設備內核引入主線Linux內核難嗎?

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

    Linux 5.4內核正式版本有哪些新功能

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

    如何查看Linux系統版本信息

    這里所謂的Linux版本信息,包括Linux內核版本信息和Linux系統
    發表于 05-19 09:11 ?3328次閱讀
    如何查看<b class='flag-5'>Linux</b>系統<b class='flag-5'>版本</b>信息

    Linux 6.1發布,微軟貢獻Linux內核代碼

    此外,公告中并沒有提及 Linux 6.1 是否是 LTS 版本。按照 Linux 內核維護者 Greg Kroah-Hartman 的說法,Lin
    的頭像 發表于 12-14 09:54 ?1246次閱讀
    主站蜘蛛池模板: 亚洲va久久久噜噜噜久久男同| 亚洲最大色网| 国产亚洲美女精品久久久2020| 天天操中文字幕| 日干夜操| 福利久久| 中文字幕在线一区二区在线| 色婷婷九月| 被cao到合不拢腿腐男男| 亚洲精品国产美女在线观看| 色综合久久综合| 美女网站视频色| 大胆国模一区二区三区伊人| 尤物久久99热国产综合| 人人干视频| 男人操女人免费网站| 日本一区二区三区视频在线观看| 综合五月婷婷| 日韩天堂在线观看| 国产香蕉视频在线播放| 午夜精品在线视频| 福利视频自拍| 流不尽奶水的大乳h| 天堂成人在线观看| 六月婷婷在线| 一级毛片女人喷潮| 久久国产免费观看| 性网站免费| 色综合激情网| 韩国视频在线播放| 手机在线你懂的| 免费xxxx大片| 性欧美视频| 精品三级在线观看| 天天艹夜夜| 在厨房乱子伦在线观看| 亚洲伊人99综合网| 久久这里只有精品1| 天天干小说| 亚洲欧美日韩一区| 色婷婷综合激情|