色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

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

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

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

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

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

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

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

tcp的關(guān)閉主要分主動(dòng)關(guān)閉、被動(dòng)關(guān)閉以及同時(shí)關(guān)閉(特殊情況,不做描述)

主動(dòng)關(guān)閉

close(fd)的過(guò)程

C語(yǔ)言為例,在我們關(guān)閉socket的時(shí)候,會(huì)使用close(fd)函數(shù):

intsocket_fd;

socket_fd = socket(AF_INET,SOCK_STREAM,0);

...

// 此處通過(guò)文件描述符關(guān)閉對(duì)應(yīng)的socket

close(socket_fd)

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

asmlinkage longsys_close(unsignedintfd)

{

// 清除(close_on_exec即退出進(jìn)程時(shí))的位圖標(biāo)記

FD_CLR(fd,fdt->close_on_exec);

// 釋放文件描述符

// 將fdt->open_fds即打開(kāi)的fd位圖中對(duì)應(yīng)的位清除

// 再將fd掛入下一個(gè)可使用的fd以便復(fù)用

__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

緊接著我們進(jìn)入fput:

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

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

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

所以在多進(jìn)程的socket服務(wù)器編寫(xiě)過(guò)程中,父進(jìn)程也需要close(fd)一次,以免socket無(wú)法最終關(guān)閉

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

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

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

f_op->release的賦值

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

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

socket_file_ops的實(shí)現(xiàn)為:

staticconststructfile_operations socket_file_ops = {

.owner = THIS_MODULE,

......

// 我們?cè)谶@里只考慮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

關(guān)于fd與socket的關(guān)系如下圖所示:

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

上圖中紅色線標(biāo)注的是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)設(shè)置為fin_wait1,并調(diào)用tcp_send_fin

voidtcp_send_fin(structsock *sk)

{

......

// 這邊設(shè)置flags為ack和fin

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

......

// 發(fā)送fin包,同時(shí)關(guān)閉nagle

__tcp_push_pending_frames(sk,mss_now,TCP_NAGLE_OFF);

}

如上圖Step1所示。 接著,主動(dòng)關(guān)閉的這一端等待對(duì)端的ACK,如果ACK回來(lái)了,就設(shè)置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設(shè)置一個(gè)TCP_FIN_WAIT2定時(shí)器,在tmo+(2MSL或者基于RTO計(jì)算超時(shí))超時(shí)后會(huì)直接變遷到closed狀態(tài)(不過(guò)此時(shí)已經(jīng)是inet_timewait_sock了)。這個(gè)超時(shí)時(shí)間可以配置,如果是ipv4的話,則可以按照下列配置:

net.ipv4.tcp_fin_timeout

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

如下圖所示:

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

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

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

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

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

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

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

voidtcp_time_wait(structsock *sk,intstate,inttimeo)

{

// 建立inet_timewait_sock

tw = inet_twsk_alloc(sk,state);

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

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

// 設(shè)置sk狀態(tài)為T(mén)CP_CLOSE,然后回收sk資源

tcp_done(sk);

}

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

被動(dòng)關(guān)閉

close_wait

在tcp的socket時(shí)候,如果是established狀態(tài),接收到了對(duì)端的FIN,則是被動(dòng)關(guān)閉狀態(tài),會(huì)進(jìn)入close_wait狀態(tài),如下圖Step1所示:

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

具體代碼如下所示:

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

我們?cè)倏聪聇cp_fin

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

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

如果對(duì)端關(guān)閉了,應(yīng)用端在read的時(shí)候得到的返回值是0,此時(shí)就應(yīng)該手動(dòng)調(diào)用close去關(guān)閉連接

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

close(sockfd)

}

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

tcp_recvmsg第一段

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

上面代碼的處理過(guò)程如下圖所示:

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

我們看下tcp_recmsg的第二段:

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

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

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

last_ack

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

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

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

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

上述代碼就是被動(dòng)關(guān)閉端的后兩次揮手了,如下圖所示:

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

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

linux中出現(xiàn)大量close_wait的情況一般是應(yīng)用在檢測(cè)到對(duì)端fin時(shí)沒(méi)有及時(shí)close當(dāng)前連接。有一種可能如下圖所示:

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

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

如果應(yīng)用close的時(shí)間過(guò)晚,對(duì)端已經(jīng)將連接給銷(xiāo)毀。則應(yīng)用發(fā)送給fin給對(duì)端,對(duì)端會(huì)由于找不到對(duì)應(yīng)的連接而發(fā)送一個(gè)RST(Reset)報(bào)文。

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

如果應(yīng)用遲遲沒(méi)有調(diào)用close_wait,那么操作系統(tǒng)有沒(méi)有一個(gè)回收機(jī)制呢,答案是有的。 tcp本身有一個(gè)包活(keep alive)定時(shí)器,在(keep alive)定時(shí)器超時(shí)之后,會(huì)強(qiáng)行將此連接關(guān)閉??梢栽O(shè)置tcp keep alive的時(shí)間

/etc/sysctl.conf

net.ipv4.tcp_keepalive_intvl = 75

net.ipv4.tcp_keepalive_probes = 9

net.ipv4.tcp_keepalive_time = 7200

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

關(guān)于tcp keepalive包活定時(shí)器可見(jiàn)筆者另一篇博客:

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

進(jìn)程關(guān)閉時(shí)清理socket資源

進(jìn)程在退出時(shí)候(無(wú)論kill,kill -9 或是正常退出)都會(huì)關(guān)閉當(dāng)前進(jìn)程中所有的fd(文件描述符)

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

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

Java GC時(shí)清理socket資源

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

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

所以Java會(huì)在GC時(shí)刻會(huì)關(guān)閉沒(méi)有被引用的socket,但是切記不要寄希望于Java的GC,因?yàn)镚C時(shí)刻并不是以未引用的socket數(shù)量來(lái)判斷的,所以有可能泄露了一堆socket,但仍舊沒(méi)有觸發(fā)GC。

總結(jié)

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

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

    關(guān)注

    87

    文章

    11312

    瀏覽量

    209691
  • Socket
    +關(guān)注

    關(guān)注

    0

    文章

    212

    瀏覽量

    34722

原文標(biāo)題:從 Linux 源碼看 socket 的 close

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

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

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

    linux socket 問(wèn)題

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

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

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

    WEB CLOSE_WAIT socket不釋放如何解決呢

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

    Linux內(nèi)核教程

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

    Linux+Socket編程

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

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

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

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

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

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

    Android雖然建立在Linux內(nèi)核之上,但是他對(duì)內(nèi)核進(jìn)行了一些擴(kuò)展,增加了一些驅(qū)動(dòng)。比如Binder,loger等等驅(qū)動(dòng)。可以拿Android內(nèi)核代碼和其Baseline
    發(fā)表于 09-09 09:10 ?4594次閱讀

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

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

    socket程序從linux移植到windows上

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

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

    Android是基于Linux內(nèi)核的操作系統(tǒng),但是,運(yùn)行在Android設(shè)備上的內(nèi)核其實(shí)與Google選擇的LTS版本Linux
    的頭像 發(fā)表于 11-22 10:41 ?3065次閱讀
    谷歌Android設(shè)備<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)核的正式版本,帶來(lái)了大量新功能,強(qiáng)化了安全,更新了硬件驅(qū)動(dòng),你值得擁有。
    的頭像 發(fā)表于 11-25 16:31 ?1w次閱讀

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

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

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

    此外,公告中并沒(méi)有提及 Linux 6.1 是否是 LTS 版本。按照 Linux 內(nèi)核維護(hù)者 Greg Kroah-Hartman 的說(shuō)法,Lin
    的頭像 發(fā)表于 12-14 09:54 ?1238次閱讀
    主站蜘蛛池模板: 俄罗斯美女z0z0z0在线| 台湾佬休闲中性娱乐网| 日日夜夜噜噜| 野花社区WWW韩国日本 | 伊人久久综合成人亚洲| 闺蜜撬开我的腿用黄瓜折磨我| 久久久久国产一级毛片高清片| 伸到同桌奶罩里捏她胸h| 1300部真实小Y女视频合集| 国产三级视频在线| 秋霞成人午夜鲁丝一区二区三区| 夜色帮首页| 国产乱人伦AV麻豆网| 飘雪在线观看免费高清完整版韩国| 夜夜躁婷婷AV蜜桃视频| 国产精品亚洲污污网站入口| 秋霞特色大片18岁入口| 2017最新伦理伦理片67| 精品国产乱码久久久久久免费流畅 | 九九大香尹人视频免费| 乌克兰女人与动ZOZO| SORA是什么意思| 榴莲黄版无限刷| 亚洲午夜久久久无码精品网红A片 亚洲午夜久久久久中文字幕 | 99久久中文字幕伊人情人| 久久www成人看片| 亚洲AV无码久久流水呻蜜桃久色 | 2021精品乱码多人收藏| 精品无码久久久久久久动漫| 无码丰满人妻熟妇区| 操中国老太太| 欧美黑大炮18p| 4484在线观看视频| 精品网站一区二区三区网站| 亚州免费一级毛片| 国产99久久久国产精品成人| 青柠高清在线观看完整版| 99RE8国产这里只有精品| 久久毛片网站| 一级毛片皇帝 宫女| 很很射影院|