内核 kmap_atomic分析
生活随笔
收集整理的這篇文章主要介紹了
内核 kmap_atomic分析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、為什么會(huì)有這個(gè)函數(shù)?
我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。
在kmap函數(shù)中,會(huì)有如下幾個(gè)比較耗時(shí)的部分:
1)、page_address函數(shù)
2)、Sleep for somebody else to unmap their entries
代碼如下:
171 ____________DECLARE_WAITQUEUE(wait, current);?
173 ______________set_current_state(TASK_UNINTERRUPTIBLE);
174____________add_wait_queue(&pkmap_map_wait,&wait);???????????????????????????????????????????????????????????????????????????
175 ____________unlock_kmap();
176 ____________schedule();
177 ____________remove_wait_queue(&pkmap_map_wait, &wait);
3)、lock_kmap函數(shù),需要加鎖啊!!!
相反kmap_atomic函數(shù),從名字就能看出,原子操作一氣呵成。。沒(méi)有sleep,沒(méi)有鎖。
2、怎么實(shí)現(xiàn)的?需要考慮什么問(wèn)題?
首先得知道這個(gè)函數(shù)的主要目的是實(shí)現(xiàn)page 到 vaddr的轉(zhuǎn)化。
其次我們得考慮多cpu和多任務(wù),大家知道???? kernel可以在多個(gè)cpu上同時(shí)運(yùn)行不同的task,然而它們共同使用一個(gè)內(nèi)存地址空間,因此如何能保證N個(gè)cpu調(diào)用kmap_atomic不會(huì)將page映射到一個(gè)虛擬地址(vaddr)呢?
我們來(lái)看看kmap_atomic是如何實(shí)現(xiàn)的?
1)、定義一個(gè)percpu變量__kmap_atomic_idx,同時(shí)在當(dāng)前cpu上禁用搶占,直到unmap的時(shí)候才開啟,這樣就保證了同一cpu其它任務(wù)不會(huì)調(diào)用該函數(shù)。除非該進(jìn)程在unmap之前睡眠,如果真的那樣,別的進(jìn)程就很可能在同一cpu重入kmap_atomic函數(shù)了,然后就可能映射到同一虛擬地址,因此在原子映射期間最好不要休眠。
2)、設(shè)計(jì)了一個(gè)完美的公式
??? type = kmap_atomic_idx_push();? //遞增一個(gè)percpu變量,返回遞增后的結(jié)果,增加一個(gè)計(jì)算type的函數(shù)表示kmap_atomic函數(shù)可以重入,就是上面說(shuō)的該進(jìn)程在unmap之前睡眠情況,但一般情況下不會(huì)發(fā)生沖入,所以該值應(yīng)該是1,unmap時(shí)該值會(huì)--。當(dāng)然重入的次數(shù)是有限制的,不會(huì)超過(guò)KM_TYPE_NR。
??? idx = type + KM_TYPE_NR*smp_processor_id();//不同的cpusmp_processor_id()的值是不同的,因此,不同的cpu得到的idx不同。
??? vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//不同的idx得到的vaddr就不同了。
??? set_pte(kmap_pte-idx, mk_pte(page, prot)); //設(shè)置頁(yè)表
??? return (void *)vaddr;
總結(jié),通過(guò)percpu變量+禁搶占+加計(jì)算公式,就實(shí)現(xiàn)了,不同cpu的不同進(jìn)程調(diào)用kmap_atomic函數(shù)得到的vaddr是不同的。同時(shí)這也給我們實(shí)現(xiàn)atomic提供了一種思路,實(shí)際上atomic一般都會(huì)有一個(gè)percpu變量。
3、得到vaddr之后,頁(yè)表的建立。
我們就以arm為例,來(lái)說(shuō)明一下,arm初始化的時(shí)候,會(huì)通過(guò)create_mapping來(lái)創(chuàng)建一些頁(yè)表,比如map_lowmem就會(huì)調(diào)用 create_mapping函數(shù)來(lái)建立整個(gè)低端內(nèi)存的映射。
同樣地在函數(shù)devicemaps_init中會(huì)有如下代碼:
?967 ____map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
?968 ____map.virtual = 0xffff0000;
?969 ____map.length = PAGE_SIZE;
?970 ____map.type = MT_HIGH_VECTORS;
?971 ____create_mapping(&map);
這里有一點(diǎn)需要說(shuō)明一點(diǎn),雖然只映射了一個(gè)PAGE_SIZE,但是如果內(nèi)核采用的二級(jí)頁(yè)表(即存在pgd,pte),而不是一級(jí)頁(yè)表(只有pgd)的話,會(huì)在create_mapping函數(shù)中,分配512個(gè)pte項(xiàng)(即512個(gè)pte指針ptep),遠(yuǎn)遠(yuǎn)大于KM_TYPE_NR個(gè)數(shù)。但是如果采用一級(jí)頁(yè)表的話,這就會(huì)有問(wèn)題,這種情況應(yīng)該不能用kmap_atomic函數(shù),因?yàn)橥ㄟ^(guò)TOP_PTE(vaddr)宏是得不到ptep的,因此目前代碼中有如下保護(hù):
?67 #ifdef CONFIG_DEBUG_HIGHMEM
?68 ____/*
?69 ____ * With debugging enabled, kunmap_atomic forces that entry to 0.
?70 ____ * Make sure it was indeed properly unmapped.
?71 ____ */
?72____BUG_ON(!pte_none((TOP_PTE(vaddr))));?????????????????????????????????????????????????????????????????????????????????????
?73 #endif
?
我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。
在kmap函數(shù)中,會(huì)有如下幾個(gè)比較耗時(shí)的部分:
1)、page_address函數(shù)
2)、Sleep for somebody else to unmap their entries
代碼如下:
171 ____________DECLARE_WAITQUEUE(wait, current);?
173 ______________set_current_state(TASK_UNINTERRUPTIBLE);
174____________add_wait_queue(&pkmap_map_wait,&wait);???????????????????????????????????????????????????????????????????????????
175 ____________unlock_kmap();
176 ____________schedule();
177 ____________remove_wait_queue(&pkmap_map_wait, &wait);
3)、lock_kmap函數(shù),需要加鎖啊!!!
相反kmap_atomic函數(shù),從名字就能看出,原子操作一氣呵成。。沒(méi)有sleep,沒(méi)有鎖。
2、怎么實(shí)現(xiàn)的?需要考慮什么問(wèn)題?
首先得知道這個(gè)函數(shù)的主要目的是實(shí)現(xiàn)page 到 vaddr的轉(zhuǎn)化。
其次我們得考慮多cpu和多任務(wù),大家知道???? kernel可以在多個(gè)cpu上同時(shí)運(yùn)行不同的task,然而它們共同使用一個(gè)內(nèi)存地址空間,因此如何能保證N個(gè)cpu調(diào)用kmap_atomic不會(huì)將page映射到一個(gè)虛擬地址(vaddr)呢?
我們來(lái)看看kmap_atomic是如何實(shí)現(xiàn)的?
1)、定義一個(gè)percpu變量__kmap_atomic_idx,同時(shí)在當(dāng)前cpu上禁用搶占,直到unmap的時(shí)候才開啟,這樣就保證了同一cpu其它任務(wù)不會(huì)調(diào)用該函數(shù)。除非該進(jìn)程在unmap之前睡眠,如果真的那樣,別的進(jìn)程就很可能在同一cpu重入kmap_atomic函數(shù)了,然后就可能映射到同一虛擬地址,因此在原子映射期間最好不要休眠。
2)、設(shè)計(jì)了一個(gè)完美的公式
??? type = kmap_atomic_idx_push();? //遞增一個(gè)percpu變量,返回遞增后的結(jié)果,增加一個(gè)計(jì)算type的函數(shù)表示kmap_atomic函數(shù)可以重入,就是上面說(shuō)的該進(jìn)程在unmap之前睡眠情況,但一般情況下不會(huì)發(fā)生沖入,所以該值應(yīng)該是1,unmap時(shí)該值會(huì)--。當(dāng)然重入的次數(shù)是有限制的,不會(huì)超過(guò)KM_TYPE_NR。
??? idx = type + KM_TYPE_NR*smp_processor_id();//不同的cpusmp_processor_id()的值是不同的,因此,不同的cpu得到的idx不同。
??? vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//不同的idx得到的vaddr就不同了。
??? set_pte(kmap_pte-idx, mk_pte(page, prot)); //設(shè)置頁(yè)表
??? return (void *)vaddr;
總結(jié),通過(guò)percpu變量+禁搶占+加計(jì)算公式,就實(shí)現(xiàn)了,不同cpu的不同進(jìn)程調(diào)用kmap_atomic函數(shù)得到的vaddr是不同的。同時(shí)這也給我們實(shí)現(xiàn)atomic提供了一種思路,實(shí)際上atomic一般都會(huì)有一個(gè)percpu變量。
3、得到vaddr之后,頁(yè)表的建立。
我們就以arm為例,來(lái)說(shuō)明一下,arm初始化的時(shí)候,會(huì)通過(guò)create_mapping來(lái)創(chuàng)建一些頁(yè)表,比如map_lowmem就會(huì)調(diào)用 create_mapping函數(shù)來(lái)建立整個(gè)低端內(nèi)存的映射。
同樣地在函數(shù)devicemaps_init中會(huì)有如下代碼:
?967 ____map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
?968 ____map.virtual = 0xffff0000;
?969 ____map.length = PAGE_SIZE;
?970 ____map.type = MT_HIGH_VECTORS;
?971 ____create_mapping(&map);
這里有一點(diǎn)需要說(shuō)明一點(diǎn),雖然只映射了一個(gè)PAGE_SIZE,但是如果內(nèi)核采用的二級(jí)頁(yè)表(即存在pgd,pte),而不是一級(jí)頁(yè)表(只有pgd)的話,會(huì)在create_mapping函數(shù)中,分配512個(gè)pte項(xiàng)(即512個(gè)pte指針ptep),遠(yuǎn)遠(yuǎn)大于KM_TYPE_NR個(gè)數(shù)。但是如果采用一級(jí)頁(yè)表的話,這就會(huì)有問(wèn)題,這種情況應(yīng)該不能用kmap_atomic函數(shù),因?yàn)橥ㄟ^(guò)TOP_PTE(vaddr)宏是得不到ptep的,因此目前代碼中有如下保護(hù):
?67 #ifdef CONFIG_DEBUG_HIGHMEM
?68 ____/*
?69 ____ * With debugging enabled, kunmap_atomic forces that entry to 0.
?70 ____ * Make sure it was indeed properly unmapped.
?71 ____ */
?72____BUG_ON(!pte_none((TOP_PTE(vaddr))));?????????????????????????????????????????????????????????????????????????????????????
?73 #endif
?
由此,我們可以看到kmap_atomic使用的是地址空間頂部的一小段地址空間(0xffff0000開始),內(nèi)核邏輯將這一小段地址空間分成了若干個(gè)節(jié)(slot),每一節(jié)的大小是一個(gè)page的大小,可以用來(lái)映射一個(gè)page。雖然總共可用512個(gè)slot,但是只能用KM_TYPE_NR個(gè),有點(diǎn)遺憾!!!
原文地址: http://blog.chinaunix.net/uid-26817832-id-3358944.html
總結(jié)
以上是生活随笔為你收集整理的内核 kmap_atomic分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux 内核的文件 Cache 管理
- 下一篇: GCC 关键字inline探究