不要盲目增加ip_conntrack_max-理解Linux核心記憶體

來源:互聯網
上載者:User
1.由ip_conntrack引出的Linux記憶體映射

有很多文章在討論關於ip_conntrack表爆滿之後丟棄資料包的問題,對此研究深入一些的知道Linux有個核心參數ip_conntrack_max,在擁有較大記憶體的機器中預設65536,於是瘋狂的增加這個參數,比如設定成10000…00,只要不報設定方面的錯誤,就一定要設定成最大值。這種方式實在是將軟體看成大神了,殊不知軟體的技術含量還不如鍋爐呢!
       如果考慮的再全面一些,比如經驗豐富的程式員或者網管,可能會想到記憶體的問題,他們知道所有的串連跟蹤資訊都是儲存於記憶體中的,因此會考慮單純放大這個ip_conntrack_max參數會佔據多少記憶體,會權衡記憶體的佔用,如果系統沒有太大的記憶體,就不會將此值設定的太高。
       但是如果你的系統有很大的記憶體呢?比如有8G的記憶體,分個1G給串連跟蹤也不算什麼啊,這是合理的,然而在傳統的32位架構Linux中是做不到,為什嗎?因為你可能根本不懂Linux核心的記憶體管理方式。
       記憶體越來越便宜的今天,linux的記憶體映射方式確實有點過時了。然而事實就擺在那裡,ip_conntrack處於核心空間,它所需的記憶體必須映射到核心空間,而傳統的32位Linux記憶體映射方式只有1G屬於核心,這1G的地址空間中,前896M是和實體記憶體一一線性映射的,後面的若干空洞之後,有若干vmalloc的空間,這些vmalloc空間和一一映射空間相比,很小很小,算上4G封頂下面的很小的映射空間,一共可以讓核心使用的地址空間不超過1G。對於ip_conntrack來講,由於其使用slab分配器,因此它還必須使用一一映射的地址空間,這就是說,它最多隻能使用不到896M的記憶體!

       為何Linux使用如此“落後”的記憶體映射機制這麼多年還不改進?其實這種對核心空間記憶體十分苛刻的設計在64位架構下有了很大的改觀,然而問題依然存在,即使64位架構,核心也無法做到透明訪問所有的實體記憶體,它同樣需要把實體記憶體映射到核心地址空間後才能訪問,對於一一映射,這種映射是事先確定的,對於大小有限(實際上很小)非一一映射空間,需要動態建立頁表,頁目錄等。另外還有一個解釋,那就是“核心本來就不該做ip_conntrack這種事”,那是協議棧的事,而不巧的是,Liunx的協議棧完全在核心中實現,可能在skb接收非強制中斷中處理的ip_conntrack不能睡眠,因此也就不能將此任務交給進程,也就不能利用進程地址空間(進程地址空間[使用者態+核心態]可以訪問所有的實體記憶體)。

       Linux之所以對核心記憶體要求如此苛刻,目的就是不想讓你隨意使用,因為它寶貴,你才更要珍惜它們。
2.在32位架構Linux系統上的實驗

以下是為了證明以上的事實所作的實驗,可能實驗中使用的一些手段仍然不符合常識,然而我覺得成一家之言即可,畢竟這種方案永遠不會也不可能出現在公司的標準文檔上,那樣會讓人學會投機取巧或者稱偷懶,但是為了備忘,還得有個地方留著,那就寫成部落格吧。
       還有一個參數會影響尋找串連跟蹤的時間複雜度和空間複雜度,那就是ip_conntrack_buckets。該值描述了雜湊桶的數量,理論上,這個值越大,雜湊碰撞就會越小,尋找時間就會越快,但是需要為每一個桶預分配一塊不是很大的記憶體,如果桶數量很大,就會佔用很大的記憶體,並且這些記憶體還都是寶貴的“僅有1G空間內的核心記憶體”,和ip_conntrack結構體的分配策略不同,這個雜湊桶可以分配在vmalloc空間而不一定非要在一一線性映射空間。
2.1.快速壓滿ip_conntrack的方法

使用loadrunner絕對是一種方式,然而術業有專攻,工作之餘我又很討厭windows上的一切,因此需要採用其它方式,下班在家,隻身一人,也不想使用netcat之類的“瑞士軍刀”,我怕連接埠佔滿,又怕我的macbook狂熱,因此需要再想辦法。由於目的只是想測試ip_conntrack最多能佔用多少記憶體,其實這個我早就知道了,只是想證實一下子,那麼辦法也就有了,那就是增加ip_conntrack結構體的大小,而這很容易,只需要在結構體後面增加一個很大的欄位即可。下面的修改基於Red Hat Enterprise 5的2.6.18核心
2.2.測試前對ip_conntrack核心模組的修改

編輯$build/include/linux/netfilter_ipv4/ip_conntrack.h檔案,在結構體ip_conntrack的最後加上下面一句:

char aaa[102400]; //這個102400是通過二分法得到的,如果設定成2xxxxx則在載入的時候就會使核心crash,因為這個數組是直接分配(類似棧上分配)的而不是動態分配的,它載入的時候很可能會衝掉核心的關鍵資料,因此還是選取一個可行的數值,然後慢慢加串連吧,畢竟擴大了起碼100000倍呢~~

進入$src/net/ipv4/netfilter,執行:

make –C /lib/modules/2.6.18-92.e15/build SUBDIRS=`pwd` modules

如此一來載入ip_conntrack.ko之後,核心日誌將列印出:
ip_conntrack version 2.4 (8192 buckets, 65536 max) - 102628 bytes per conntrack
由此看出ip_conntrack結構體已經增大了,這樣撐滿整個可用記憶體所需的網路連接壓力就大大減小了,也就不用什麼loadrunner之類的東西了。為了儘快撐滿可以使用的記憶體,還要將關於ip_conntrack的所有timeout設定的比較長,相當長:

sysctl -w net.ipv4.netfilter.ip_conntrack_generic_timeout=600000sysctl -w net.ipv4.netfilter.ip_conntrack_icmp_timeout=300000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close=1000000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack=300000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=432000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv=600000sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent=120000

這樣既有的一個流就會“永久保持”了,一直佔著ip_conntrack結構體不放,直到可用的記憶體溢出。
       在載入了ip_conntrack模組之後,所有過往的資料包就會自動被追蹤,下面編寫以下指令碼:

for (( i=1; i<255; i++));do    for (( j=1; j<255; j++));    do        ping 192.168.$i.$j -c 1 -W 1        curl --connect-timeout 1 http://138.$i.$j.80/        curl --connect-timeout 1 http://38.$i.$j.80/        curl --connect-timeout 1 http://$i.1.$j.80/        curl --connect-timeout 1 http://$j.$i.9.8/    donedone
2.3.測試過程

本機配置:
記憶體:3032M,free命令識別3003M
執行上述指令碼,抽根煙,拉一脬(pao),得到下列的資料:
封頂串連數:6149個
使用記憶體:886M
此時在本機ping 127.0.0.1也不通了,說明ip_conntrack已經達到了極限,同時由於在alloc ip_conntrack的地方插入了列印語句(肯定是一堆#號),核心列印了記憶體配置失敗的資訊。一共3G的記憶體,僅僅使用了886M(而且我不斷使用sysctl –w vm.drop_caches=3清楚cache),剩餘的都無法給ip_conntrack使用。為了使結果更有說服力,我在ip_conntrack模組的初始化函數中插入了下列代碼:

for (j=0; I < 400; j++)    __get_free_pages(GFP_KERNEL, 8);

意思是我先佔去核心空間的400M記憶體,看看最終總的串連跟蹤數量是否也會減少相應的,得到資料如下:
封頂串連數:3421個
使用記憶體:879M
可見,核心記憶體被額外佔據了,能分給ip_conntrack的就少了。更進一步,保持上述的__get_free_pages不變,再增加下列的代碼:

for (j=0; I < 400; j++)    __get_free_pages(GFP_HIGHUSER, 8);

最終的結果如下:
封頂串連數:3394個
使用記憶體:1203M
可見,HIGHUSER記憶體並不會怎麼影響核心記憶體,要知道使用者進程的記憶體幾乎都是使用這個HIGHUSER標識分配的。如果去掉GFP_KERNEL的分配,僅僅保留GFP_HIGHUSER的分配,得到下列結果:
封頂串連數:6449個
使用記憶體:1312M
可見,HIGHUSER記憶體的分配儘力在高端進行,不會怎麼影響核心的一一映射空間。
2.4.測試結果

綜上所述,32位架構上Linux的ip_conntrack使用的記憶體只能在核心地址空間的一一映射區,換句話說,它只能使用實體記憶體的前896M,除掉ip_conntrack結構體的添加的char aaa[],也是這個結果,只不過要想壓滿所有的可用記憶體,不是很容易,需要動用幾台機器以及loadrunner之類的壓力工具。
3.在64位架構Linux系統上做的實驗

MD!由於我大動了核心資料結構,載入模組沒有成功,至今仍在調試,排錯,已幾個時辰有餘…
4.結論

最後,需要說明的是,ip_conntrack_max的初始值是核心根據你機器的記憶體計算出來的,包括ip_conntrack_buckets也是這樣算出來的,核心之所以設定這樣的初始值,那是經過精心測試的經驗值,因此除了非要改不可,不要去提高這個值。如果你的機器面臨大量串連,你提高了ip_conntrack_max的值,那麼代價就是佔用了大量可貴的核心記憶體,可能會引起其它的核心記憶體配置失敗,並且,一旦核心記憶體使用量超過了核心記憶體空間映射的閥值,那麼系統會默默的丟棄你的資料包,而不會報出:
table full, dropping packet error and solution
之類的錯誤,這是可悲的一件事。

       作業系統核心影響協議棧行為帶來了一種錯覺:我有這麼多記憶體,為何不讓我使用?!事實上,不是你要使用,而是核心要使用,你可以控制的只是進程,對於核心,程式員是沒法控制的。當然你可以重新編譯,甚至修改核心,甚至修改ip_conntrack的記憶體配置方式,不再使用夥伴系統的slab記憶體,而是重新導向一個userspace,然則,然則這個開發需要成本,一個人日幾百塊,沒有幾個公司願意花這筆錢。因此,最終的結論:不要盲目增加ip_conntrack_max。呼應了題目。

附:1.關於GFP_XXX

GFP_ATOMIC:設定這個標識,在核心映射地區的緊急池分配,不成功則簡單返回NULL,不釋放其它記憶體,也就不kill進程,分配路徑不睡眠。ip_conntrack結構體就是使用這個標識分配的(它大多數處於非強制中斷路徑)
GFP_KERNEL:設定這個標識,在核心映射地區分配,不成功則嘗試釋放可以釋放的記憶體,嘗試調用oom_killer,可以睡眠
GFP_HIGHUSER:設定這個標識,可以分配到所有的實體記憶體(排除很小一部分固定記憶體以及用於匯流排IO的記憶體,這個在x86上很明顯,具體參見/proc/iomem獲得詳細實體記憶體映射資訊)。一般而言,使用者態進程所需的記憶體都使用這個標識來分配,盡量使用核心“很難”使用的高端記憶體(1G以後的實體記憶體,核心還要動態映射,這要操作頁表),從而將前896M(32位架構)盡量留給核心使用    

2.關於ip_conntrack的雜湊

Linux的ip_conntrack使用了 jhash函數來進行雜湊計算,關於該函數的實現可以參考下面這個網址:
http://burtleburtle.net/bob/hash/doobs.html
作者的解釋很清晰

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.