电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

分析從Linux源碼看TIME_WAIT的持續時間

瀏覽:185日期:2024-04-09 08:26:35
目錄一、前言二、首先介紹下Linux環境三、TIME_WAIT狀態轉移圖四、持續時間真如TCP_TIMEWAIT_LEN所定義么?五、TIME_WAIT定時器源碼5.1、inet_twsk_schedule5.2、具體的清理函數5.3、先作出一個假設5.4、如果一個slot中的TIME_WAIT<=1005.5、如果一個slot中的TIME_WAIT>1005.6、PAWS(Protection Against Wrapped Sequences)使得TIME_WAIT延長一、前言

筆者一直以為在Linux下TIME_WAIT狀態的Socket持續狀態是60s左右。線上實際卻存在TIME_WAIT超過100s的Socket。由于這牽涉到最近出現的一個復雜Bug的分析。所以,筆者就去Linux源碼里面,一探究竟。

二、首先介紹下Linux環境

TIME_WAIT這個參數通常和五元組重用扯上關系。在這里,筆者先給出機器的內核參數設置,以免和其它問題相混淆。

cat /proc/sys/net/ipv4/tcp_tw_reuse 0

cat /proc/sys/net/ipv4/tcp_tw_recycle 0

cat /proc/sys/net/ipv4/tcp_timestamps 1

可以看到,我們設置了tcp_tw_recycle為0,這可以避免NAT下tcp_tw_recycle和tcp_timestamps同時開啟導致的問題。

三、TIME_WAIT狀態轉移圖

提到Socket的TIME_WAIT狀態,不得就不亮出TCP狀態轉移圖了:

分析從Linux源碼看TIME_WAIT的持續時間

持續時間就如圖中所示的2MSL。但圖中并沒有指出2MSL到底是多長時間,但筆者從Linux源碼里面翻到了下面這個宏定義。

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT * state, about 60 seconds*/

如英文字面意思所示,60s后銷毀TIME_WAIT狀態,那么2MSL肯定就是60s嘍?

四、持續時間真如TCP_TIMEWAIT_LEN所定義么?

筆者之前一直是相信60秒TIME_WAIT狀態的socket就能夠被Kernel回收的。甚至筆者自己做實驗telnet一個端口號,人為制造TIME_WAIT,自己計時,也是60s左右即可回收。

分析從Linux源碼看TIME_WAIT的持續時間

但在追查一個問題時候,發現,TIME_WAIT有時候能夠持續到111s,不然完全無法解釋問題的現象。這就逼得筆者不得不推翻自己的結論,重新細細閱讀內核對于TIME_WAIT狀態處理的源碼。當然,這個追查的問題也會寫成博客分享出來,敬請期待_。

五、TIME_WAIT定時器源碼

談到TIME_WAIT何時能夠被回收,不得不談到TIME_WAIT定時器,這個就是專門用來銷毀到期的TIME_WAIT Socket的。而每一個Socket進入TIME_WAIT時,必然會經過下面的代碼分支:

tcp_v4_rcv

|->tcp_timewait_state_process

/* 將time_wait狀態的socket鏈入時間輪

|->inet_twsk_schedule

由于我們的kernel并沒有開啟tcp_tw_recycle,所以最終的調用為:

/* 這邊TCP_TIMEWAIT_LEN 60 * HZ */inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN, TCP_TIMEWAIT_LEN);

好了,讓我們按下這個核心函數吧。

5.1、inet_twsk_schedule

在閱讀源碼前,先看下大致的處理流程。Linux內核是通過時間輪來處理到期的TIME_WAIT socket,如下圖所示:

分析從Linux源碼看TIME_WAIT的持續時間

內核將60s的時間分為8個slot(INET_TWDR_RECYCLE_SLOTS),每個slot處理7.5(60/8)范圍time_wait狀態的socket。

void inet_twsk_schedule(struct inet_timewait_sock *tw,struct inet_timewait_death_row *twdr,const int timeo, const int timewait_len){......// 計算時間輪的slotslot = (timeo + (1 << INET_TWDR_RECYCLE_TICK) - 1) >> INET_TWDR_RECYCLE_TICK;......// 慢時間輪的邏輯,由于沒有開啟TCP_TW_RECYCLE,timeo總是60*HZ(60s)// 所有都走slow_timer邏輯 if (slot >= INET_TWDR_RECYCLE_SLOTS) {/* Schedule to slow timer */if (timeo >= timewait_len) {slot = INET_TWDR_TWKILL_SLOTS - 1;} else {slot = DIV_ROUND_UP(timeo, twdr->period);if (slot >= INET_TWDR_TWKILL_SLOTS)slot = INET_TWDR_TWKILL_SLOTS - 1;}tw->tw_ttd = jiffies + timeo;// twdr->slot當前正在處理的slot// 在TIME_WAIT_LEN下,這個邏輯一般7slot = (twdr->slot + slot) & (INET_TWDR_TWKILL_SLOTS - 1);list = &twdr->cells[slot];} else{// 走短時間定時器,由于篇幅原因,不在這里贅述......}....../* twdr->period 60/8=7.5 */if (twdr->tw_count++ == 0)mod_timer(&twdr->tw_timer, jiffies + twdr->period);spin_unlock(&twdr->death_lock);}

從源碼中可以看到,由于我們傳入的timeout皆為TCP_TIMEWAIT_LEN。所以,每次剛成為的TIME_WAIT狀態的socket即將鏈接到當前處理slot最遠的slot(+7)以便處理。如下圖所示:

分析從Linux源碼看TIME_WAIT的持續時間

如果Kernel不停的產生TIME_WAIT,那么整個slow timer時間輪就會如下圖所示:

分析從Linux源碼看TIME_WAIT的持續時間

所有的slot全部掛滿了TIME_WAIT狀態的Socket。

5.2、具體的清理函數

每次調用inet_twsk_schedule時候傳入的處理函數都是:

/*參數中的tcp_death_row即為承載時間輪處理函數的結構體*/inet_twsk_schedule(tw,&tcp_death_row,TCP_TIMEWAIT_LEN,TCP_TIMEWAIT_LEN)/* 具體的處理結構體 */struct inet_timewait_death_row tcp_death_row = {....../* slow_timer時間輪處理函數 */.tw_timer= TIMER_INITIALIZER(inet_twdr_hangman, 0, (unsigned long)&tcp_death_row),/* slow_timer時間輪輔助處理函數*/.twkill_work= __WORK_INITIALIZER(tcp_death_row.twkill_work, inet_twdr_twkill_work),/* 短時間輪處理函數 */.twcal_timer= TIMER_INITIALIZER(inet_twdr_twcal_tick, 0, (unsigned long)&tcp_death_row),};

由于我們這邊主要考慮的是設置為TCP_TIMEWAIT_LEN(60s)的處理時間,所以直接考察slow_timer時間輪處理函數,也就是inet_twdr_hangman。這個函數還是比較簡短的:

void inet_twdr_hangman(unsigned long data){struct inet_timewait_death_row *twdr;unsigned int need_timer;twdr = (struct inet_timewait_death_row *)data;spin_lock(&twdr->death_lock);if (twdr->tw_count == 0)goto out;need_timer = 0;// 如果此slot處理的time_wait socket已經達到了100個,且還沒處理完if (inet_twdr_do_twkill_work(twdr, twdr->slot)) {twdr->thread_slots |= (1 << twdr->slot);// 將余下的任務交給work queue處理schedule_work(&twdr->twkill_work);need_timer = 1;} else {/* We purged the entire slot, anything left? */// 判斷是否還需要繼續處理if (twdr->tw_count)need_timer = 1;// 如果當前slot處理完了,才跳轉到下一個slottwdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));}// 如果還需要繼續處理,則在7.5s后再運行此函數if (need_timer)mod_timer(&twdr->tw_timer, jiffies + twdr->period);out:spin_unlock(&twdr->death_lock);}

雖然簡單,但這個函數里面有不少細節。第一個細節,就在inet_twdr_do_twkill_work,為了防止這個slot的time_wait過多,卡住當前的流程,其會在處理完100個time_wait socket之后就回返回。這個slot余下的time_wait會交給Kernel的work_queue機制去處理。

分析從Linux源碼看TIME_WAIT的持續時間

值得注意的是。由于在這個slow_timer時間輪判斷里面,根本不判斷精確時間,直接全部刪除。所以輪到某個slot,例如到了52.5-60s這個slot,直接清理52.5-60s的所有time_wait。即使time_wait還沒有到60s也是如此。而小時間輪(tw_cal)會精確的判定時間,由于篇幅原因,就不在這里細講了。

注: 小時間輪(tw_cal)在tcp_tw_recycle開啟的情況下會使用

5.3、先作出一個假設

我們假設,一個時間輪的數據最多能在一個slot間隔時間,也就是(60/8=7.5)內肯定能處理完畢。由于系統有tcp_tw_max_buckets設置,如果設置的比較合理,這個假設還是比較靠譜的。

注: 這里的60/8為什么需要精確到小數,而不是7。

因為實際計算的時候是拿60*HZ進行計算,

如果HZ是1024的話,那么period應該是7680,即精度精確到ms級。

所以在本文中計算的時候需要精確到小數。

5.4、如果一個slot中的TIME_WAIT<=100

如果一個slot的TIME_WAIT<=100,很自然的,我們的處理函數并不會啟用work_queue。同時,還將slot+1,使得在下一個period的時候可以處理下一個slot。如下圖所示:

分析從Linux源碼看TIME_WAIT的持續時間

5.5、如果一個slot中的TIME_WAIT>100

如果一個slot的TIME_WAIT>100,Kernel會將余下的任務交給work_queue處理。同時,slot不變!也即是說,下一個period(7.5s后)到達的時候,還會處理同樣的slot。按照我們的假設,這時候slot已經處理完畢,那么在第7.5s的時候才將slot向前推進。也就是說,假設slot一開始為0,到真正處理slot 1需要15s!

分析從Linux源碼看TIME_WAIT的持續時間

假設每一個slot的TIME_WAIT都>100的話,那么每個slot的處理都需要15s。

對于這種情況,筆者寫了個程序進行模擬。

public class TimeWaitSimulator { public static void main(String[] args) {double delta = (60) * 1.0 / 8;// 0表示開始清理,1表示清理完畢// 清理完畢之后slot向前推進int startPurge = 0;double sum = 0;int slot = 0;while (slot < 8) { if (startPurge == 0) {sum += delta;startPurge = 1;if (slot == 7) { // 因為假設進入work_queue之后,很快就會清理完 // 所以在slot為7的時候并不需要等最后的那個purge過程7.5s System.out.println('slot ' + slot + ' has reach the last ' + sum); break;} } if (startPurge == 1) {sum += delta;startPurge = 0;System.out.println('slot ' + 'move to next at time ' + sum);// 清理完之后,slot才應該向前推進slot++; }} }}

得出結果如下面所示:

slot move to next at time 15.0

slot move to next at time 30.0

slot move to next at time 45.0

slot move to next at time 60.0

slot move to next at time 75.0

slot move to next at time 90.0

slot move to next at time 105.0

slot 7 has reach the last  112.5

也即處理到52.5-60s這個時間輪的時候,其實外面時間已經過去了112.5s,處理已經完全滯后了。不過由于TIME_WAIT狀態下的Socket(inet_timewait_sock)所占用內存很少,所以不會對系統可用資源造成太大的影響。但是,這會在NAT環境下造成一個坑,這也是筆者文章前面提到過的Bug。上面的計算如果按照圖和時間線畫出來,應該是這么個情況:

分析從Linux源碼看TIME_WAIT的持續時間

也即TIME_WAIT狀態的Socket在一個period(7.5s)內能處理完當前slot的情況下,最多能夠存在112.5s!

如果7.5s內還處理不完,那么響應時間輪的輪轉還得繼續加上一個或多個perod。但在tcp_tw_max_buckets的限制,應該無法達到這么嚴苛的條件。

5.6、PAWS(Protection Against Wrapped Sequences)使得TIME_WAIT延長

事實上,以上結論還是不夠嚴謹。TIME_WAIT時間還可以繼續延長!看下這段源碼:

enum tcp_tw_statustcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, const struct tcphdr *th){......if (paws_reject)NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_PAWSESTABREJECTED);if (!th->rst) {/* In this case we must reset the TIMEWAIT timer. * * If it is ACKless SYN it may be both old duplicate * and new good SYN with random sequence number <rcv_nxt. * Do not reschedule in the last case. *//* 如果有回繞校驗失敗的包到達的情況下,或者其實ack包 * 重置定時器到新的60s之后 * /if (paws_reject || th->ack)inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN, TCP_TIMEWAIT_LEN);/* Send ACK. Note, we do not put the bucket, * it will be released by caller. *//* 向對端發送當前time wait狀態應該返回的ACK */return TCP_TW_ACK;}inet_twsk_put(tw);/* 注意,這邊通過paws校驗的包,會返回tcp_tw_success,使得time_wait狀態的 * socket五元組也可以三次握手成功重新復用 * /return TCP_TW_SUCCESS;}

上面的邏輯如下圖所示:

分析從Linux源碼看TIME_WAIT的持續時間

注意代碼最后的return TCP_TW_SUCCESS,通過PAWS校驗的包,會返回TCP_TW_SUCCESS,使得TIME_WAIT狀態的Socket(五元組)也可以三次握手成功重新復用!

以上就是分析從Linux源碼看TIME_WAIT的持續時間的詳細內容,更多關于Linux源碼 TIME_WAIT持續時間的資料請關注好吧啦網其它相關文章!

標簽: Linux系統
相關文章:
主站蜘蛛池模板: 轴承振动测量仪电箱-轴承测振动仪器-测试仪厂家-杭州居易电气 | TPE_TPE热塑性弹性体_TPE原料价格_TPE材料厂家-惠州市中塑王塑胶制品公司- 中塑王塑胶制品有限公司 | 电伴热系统施工_仪表电伴热保温箱厂家_沃安电伴热管缆工业技术(济南)有限公司 | 大型工业风扇_工业大风扇_大吊扇_厂房车间降温-合昌大风扇 | 低合金板|安阳低合金板|河南低合金板|高强度板|桥梁板_安阳润兴 北京租车牌|京牌指标租赁|小客车指标出租 | 废气处理_废气处理设备_工业废气处理_江苏龙泰环保设备制造有限公司 | 空压机商城|空气压缩机|空压机配件-压缩机网旗下商城 | 2025黄道吉日查询、吉时查询、老黄历查询平台- 黄道吉日查询网 | 光环国际-新三板公司_股票代码:838504 | 天津试验仪器-电液伺服万能材料试验机,恒温恒湿标准养护箱,水泥恒应力压力试验机-天津鑫高伟业科技有限公司 | 网站建设,北京网站建设,北京网站建设公司,网站系统开发,北京网站制作公司,响应式网站,做网站公司,海淀做网站,朝阳做网站,昌平做网站,建站公司 | 卫生型双针压力表-高温防腐差压表-安徽康泰电气有限公司 | 数控专用机床,专用机床,自动线,组合机床,动力头,自动化加工生产线,江苏海鑫机床有限公司 | 鑫达滑石-辽宁鑫达滑石集团 | 安徽控制器-合肥船用空调控制器-合肥家电控制器-合肥迅驰电子厂 安徽净化板_合肥岩棉板厂家_玻镁板厂家_安徽科艺美洁净科技有限公司 | RO反渗透设备_厂家_价格_河南郑州江宇环保科技有限公司 | 定制液氮罐_小型气相液氮罐_自增压液氮罐_班德液氮罐厂家 | 石家庄网站建设|石家庄网站制作|石家庄小程序开发|石家庄微信开发|网站建设公司|网站制作公司|微信小程序开发|手机APP开发|软件开发 | 电动葫芦|手拉葫芦|环链电动葫芦|微型电动葫芦-北京市凌鹰起重机械有限公司 | 无线遥控更衣吊篮_IC卡更衣吊篮_电动更衣吊篮配件_煤矿更衣吊篮-力得电子 | 铣刨料沥青破碎机-沥青再生料设备-RAP热再生混合料破碎筛分设备 -江苏锡宝重工 | 广州迈驰新GMP兽药包装机首页_药品包装机_中药散剂包装机 | 杰福伦_磁致伸缩位移传感器_线性位移传感器-意大利GEFRAN杰福伦-河南赉威液压科技有限公司 | 步入式高低温测试箱|海向仪器 | 招商帮-一站式网络营销服务|互联网整合营销|网络推广代运营|信息流推广|招商帮企业招商好帮手|搜索营销推广|短视视频营销推广 | 彼得逊采泥器-定深式采泥器-电动土壤采样器-土壤样品风干机-常州索奥仪器制造有限公司 | 消防设施操作员考试报名时间,报名入口,报考条件 | 京马网,京马建站,网站定制,营销型网站建设,东莞建站,东莞网站建设-首页-京马网 | 托利多电子平台秤-高精度接线盒-托利多高精度电子秤|百科 | 南京兰江泵业有限公司-水解酸化池潜水搅拌机-絮凝反应池搅拌机-好氧区潜水推进器 | 陕西安玻璃自动感应门-自动重叠门-磁悬浮平开门厂家【捷申达门业】 | 即用型透析袋,透析袋夹子,药敏纸片,L型涂布棒-上海桥星贸易有限公司 | 散热器-电子散热器-型材散热器-电源散热片-镇江新区宏图电子散热片厂家 | 安规_综合测试仪,电器安全性能综合测试仪,低压母线槽安规综合测试仪-青岛合众电子有限公司 | 合肥网络推广_合肥SEO网站优化-安徽沃龙First | 视频教程导航网_视频教程之家_视频教程大全_最新视频教程分享发布平台 | 生产自动包装秤_颗粒包装秤_肥料包装秤等包装机械-郑州鑫晟重工科技有限公司 | 上海璟文空运首页_一级航空货运代理公司_机场快递当日达 | 济南ISO9000认证咨询代理公司,ISO9001认证,CMA实验室认证,ISO/TS16949认证,服务体系认证,资产管理体系认证,SC食品生产许可证- 济南创远企业管理咨询有限公司 郑州电线电缆厂家-防火|低压|低烟无卤电缆-河南明星电缆 | 超声波成孔成槽质量检测仪-压浆机-桥梁预应力智能张拉设备-上海硕冠检测设备有限公司 | 一技任务网_有一技之长,就来技术任务网|