本文目的
本文補(bǔ)充校正一些Linux內(nèi)核開(kāi)發(fā)者關(guān)于GFP_ATOMIC的認(rèn)知不完整的地方,闡述GFP_ATOMIC與free內(nèi)存watermark的關(guān)系,并明確什么時(shí)候應(yīng)該用GFP_ATOMIC申請(qǐng)內(nèi)存。目錄:
1. GFP_ATOMICvs. GFP_KERNEL
2. 內(nèi)存水位,PF_MEMALLOC和GFP_ATOMIC
3. 何時(shí)使用GFP_ATOMIC(一個(gè)patch分析)
GFP_ATOMICvs. GFP_KERNEL
我們都知道,在中斷、軟中斷、spinlock等原子上下文里面,申請(qǐng)內(nèi)存,應(yīng)該使用GFP_ATOMIC標(biāo)記,譬如內(nèi)核中有大量的kmalloc/GFP_ATOMIC的例子:
對(duì)于不可睡眠的上下文,如果我們用常規(guī)的GFP_KERNEL這樣的標(biāo)記去申請(qǐng)內(nèi)存,可能引發(fā)直接的內(nèi)存reclaim,從而引起睡眠,所以GFP_KERNEL這種標(biāo)記只適合進(jìn)程上下文調(diào)用:
GFP_KERNEL的標(biāo)記可以引發(fā)直接的內(nèi)存回收,從而導(dǎo)致進(jìn)程阻塞睡眠,這在原子上下文顯然是不允許的。
#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) #define __GFP_RECLAIM ((__force gfp_t)(___GFP_DIRECT_RECLAIM|___GFP_KSWAPD_RECLAIM)
內(nèi)存水位,PF_MEMALLOC和GFP_ATOMIC
那么GFP_ATOMIC是否僅僅意味著不能睡眠呢?檔案是否定的,GFP_ATOMIC還與內(nèi)存reclaim的水位相關(guān)。下面這個(gè)圖是講述水位watermark的一個(gè)著名的圖,筆者懶得畫(huà)了,直接從網(wǎng)下copy過(guò)來(lái):
在Linux中,內(nèi)存有3個(gè)水位:
HIGH:系統(tǒng)的free內(nèi)存大于HIGH水位的時(shí)候,是一個(gè)相對(duì)保險(xiǎn)的值,不需要急著做內(nèi)存回收(reclaim);
LOW: 系統(tǒng)的free內(nèi)存達(dá)到LOW水位的時(shí)候,啟動(dòng)后臺(tái)kswapd進(jìn)行內(nèi)存回收,回收的目標(biāo)是讓空閑內(nèi)存達(dá)到HIGH水位;
MIN:系統(tǒng)應(yīng)該保有的最小free內(nèi)存,當(dāng)空閑內(nèi)存達(dá)到這個(gè)值的時(shí)候,kswapd的后臺(tái)回收可能來(lái)不及了,一般用戶在申請(qǐng)內(nèi)存的時(shí)候,進(jìn)行DIRECT RECLAIM。
min水位一般是系統(tǒng)自動(dòng)換算的,其具體值可以從/proc看出:
# cat /proc/sys/vm/min_free_kbytes 45056
而LOW水位一般是min*125%,HIGH 一般是min*150%。
MIN水位以下的內(nèi)存,只能被緊急情況下的用戶申請(qǐng)到,最著名的緊急用戶莫過(guò)于PF_MEMALLOC用戶,task_struct設(shè)置了這個(gè)標(biāo)記表示忽略MIN水位。比如回收內(nèi)存的代碼本身也可能需要申請(qǐng)內(nèi)存,這個(gè)時(shí)候我們應(yīng)該給它無(wú)限制的申請(qǐng)能力。典型地,比如kswapd就設(shè)置了這個(gè)標(biāo)記,這個(gè)代碼里面的注釋也非常精彩:
如果我們不允許回收內(nèi)存的代碼申請(qǐng)min以下的內(nèi)存,則回收內(nèi)存的代碼可以觸發(fā)回收內(nèi)存,這樣“子子孫孫,無(wú)窮匱也”。
當(dāng)然,PF_MEMALLOC不是唯一的緊急用戶,GFP_ATOMIC實(shí)際也是一個(gè)“半緊急”任務(wù):
說(shuō)它“緊急”,是因?yàn)槿绻由舷挛纳暾?qǐng)內(nèi)存失敗,往往意味著相應(yīng)的中斷、軟中斷、spinlock內(nèi)部的代碼就會(huì)執(zhí)行失敗,而我們又不會(huì)因?yàn)檫@種失敗,而去嘗試內(nèi)存回收,這顯然比較慘,我們應(yīng)該盡可能讓GFP_ATOMIC申請(qǐng)成功;
說(shuō)它“半”,是因?yàn)樗恢劣诰o急到PF_MEMALLOC這個(gè)程度,如果我們給它無(wú)限地申請(qǐng)到free內(nèi)存為0的權(quán)力,則會(huì)導(dǎo)致PF_MEMALLOC沒(méi)有內(nèi)存了。想想,如果征糧隊(duì)的人都餓死了,還怎么去征糧呢?
所以,內(nèi)存的設(shè)計(jì)選擇是,當(dāng)有人用GFP_ATOMIC申請(qǐng)內(nèi)存的時(shí)候,允許它從MIN水位以下,申請(qǐng)一定數(shù)量的內(nèi)存。什么叫“一定數(shù)量”呢?就是不能讓GFP_ATOMIC導(dǎo)致free 內(nèi)存觸底,GFP_ATOMIC還包含了高優(yōu)先級(jí)的含義:
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
注意這個(gè)里面的__GFP_HIGH不是HIGHMEM高端內(nèi)存的意思,而是高優(yōu)先級(jí)。
當(dāng)我們用GFP_ATOMIC申請(qǐng)內(nèi)存的時(shí)候,內(nèi)核的水位檢查代碼,會(huì)允許我們觸及到MIN水位以下的1/2:
那么,“魔鬼”就是在畫(huà)紅圈的2行代碼。但是,如果我們進(jìn)一步深究,會(huì)發(fā)現(xiàn),GFP_ATOMIC不只是觸及1/2*min,它甚至可以觸及1/4*min,因?yàn)镚FP_ATOMIC中的__GFP_HIGH讓ALLOC_HIGH成立,而__GFP_ATOMIC讓ALLOC_HARDER成立:
所以,“魔鬼”又隱藏在了gfp_to_alloc_flags()的細(xì)節(jié)里。
一個(gè)patch的例子
在具體的工程實(shí)戰(zhàn)中,我們建議:
原子上下文使用GFP_ATOMIC
比如在網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)drivers/net/ethernet中,就有大量的案例
在內(nèi)存緊急的路徑上(比如不想睡眠,要求低延遲;或者要求內(nèi)存吃緊的情況下,仍然可以從min水位以下申請(qǐng)內(nèi)存),哪怕是進(jìn)程上下文,我們也建議可以考慮使用GFP_ATOMIC
比如田濤童鞋最近在mm/zswap.c發(fā)的RFC patch:
https://lore.kernel.org/linux-mm/1608894171-54174-2-git-send-email-tiantao6@hisilicon.com/
上面2個(gè)地方,其實(shí)都是可以睡眠的進(jìn)程上下文,但是我們認(rèn)為在frontendswap的路徑上,我們對(duì)延遲敏感,對(duì)swap內(nèi)存過(guò)程中進(jìn)一步引發(fā)內(nèi)存回收也擔(dān)憂,因此,這里哪怕是非原子上下文,我們也沒(méi)有使用GFP_KERNEL。
責(zé)任編輯:xj
原文標(biāo)題:宋寶華:Linux內(nèi)核中用GFP_ATOMIC申請(qǐng)內(nèi)存究竟意味著什么?
文章出處:【微信公眾號(hào):Linuxer】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1379瀏覽量
40354 -
Linux
+關(guān)注
關(guān)注
87文章
11335瀏覽量
210068 -
GFP
+關(guān)注
關(guān)注
0文章
5瀏覽量
1420
原文標(biāo)題:宋寶華:Linux內(nèi)核中用GFP_ATOMIC申請(qǐng)內(nèi)存究竟意味著什么?
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論