内核 kmap_atomic分析
生活随笔
收集整理的這篇文章主要介紹了
内核 kmap_atomic分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、為什么會有這個函數?
我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。
在kmap函數中,會有如下幾個比較耗時的部分:
1)、page_address函數
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函數,需要加鎖啊!!!
相反kmap_atomic函數,從名字就能看出,原子操作一氣呵成。。沒有sleep,沒有鎖。
2、怎么實現的?需要考慮什么問題?
首先得知道這個函數的主要目的是實現page 到 vaddr的轉化。
其次我們得考慮多cpu和多任務,大家知道???? kernel可以在多個cpu上同時運行不同的task,然而它們共同使用一個內存地址空間,因此如何能保證N個cpu調用kmap_atomic不會將page映射到一個虛擬地址(vaddr)呢?
我們來看看kmap_atomic是如何實現的?
1)、定義一個percpu變量__kmap_atomic_idx,同時在當前cpu上禁用搶占,直到unmap的時候才開啟,這樣就保證了同一cpu其它任務不會調用該函數。除非該進程在unmap之前睡眠,如果真的那樣,別的進程就很可能在同一cpu重入kmap_atomic函數了,然后就可能映射到同一虛擬地址,因此在原子映射期間最好不要休眠。
2)、設計了一個完美的公式
??? type = kmap_atomic_idx_push();? //遞增一個percpu變量,返回遞增后的結果,增加一個計算type的函數表示kmap_atomic函數可以重入,就是上面說的該進程在unmap之前睡眠情況,但一般情況下不會發生沖入,所以該值應該是1,unmap時該值會--。當然重入的次數是有限制的,不會超過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)); //設置頁表
??? return (void *)vaddr;
總結,通過percpu變量+禁搶占+加計算公式,就實現了,不同cpu的不同進程調用kmap_atomic函數得到的vaddr是不同的。同時這也給我們實現atomic提供了一種思路,實際上atomic一般都會有一個percpu變量。
3、得到vaddr之后,頁表的建立。
我們就以arm為例,來說明一下,arm初始化的時候,會通過create_mapping來創建一些頁表,比如map_lowmem就會調用 create_mapping函數來建立整個低端內存的映射。
同樣地在函數devicemaps_init中會有如下代碼:
?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);
這里有一點需要說明一點,雖然只映射了一個PAGE_SIZE,但是如果內核采用的二級頁表(即存在pgd,pte),而不是一級頁表(只有pgd)的話,會在create_mapping函數中,分配512個pte項(即512個pte指針ptep),遠遠大于KM_TYPE_NR個數。但是如果采用一級頁表的話,這就會有問題,這種情況應該不能用kmap_atomic函數,因為通過TOP_PTE(vaddr)宏是得不到ptep的,因此目前代碼中有如下保護:
?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函數中,會有如下幾個比較耗時的部分:
1)、page_address函數
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函數,需要加鎖啊!!!
相反kmap_atomic函數,從名字就能看出,原子操作一氣呵成。。沒有sleep,沒有鎖。
2、怎么實現的?需要考慮什么問題?
首先得知道這個函數的主要目的是實現page 到 vaddr的轉化。
其次我們得考慮多cpu和多任務,大家知道???? kernel可以在多個cpu上同時運行不同的task,然而它們共同使用一個內存地址空間,因此如何能保證N個cpu調用kmap_atomic不會將page映射到一個虛擬地址(vaddr)呢?
我們來看看kmap_atomic是如何實現的?
1)、定義一個percpu變量__kmap_atomic_idx,同時在當前cpu上禁用搶占,直到unmap的時候才開啟,這樣就保證了同一cpu其它任務不會調用該函數。除非該進程在unmap之前睡眠,如果真的那樣,別的進程就很可能在同一cpu重入kmap_atomic函數了,然后就可能映射到同一虛擬地址,因此在原子映射期間最好不要休眠。
2)、設計了一個完美的公式
??? type = kmap_atomic_idx_push();? //遞增一個percpu變量,返回遞增后的結果,增加一個計算type的函數表示kmap_atomic函數可以重入,就是上面說的該進程在unmap之前睡眠情況,但一般情況下不會發生沖入,所以該值應該是1,unmap時該值會--。當然重入的次數是有限制的,不會超過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)); //設置頁表
??? return (void *)vaddr;
總結,通過percpu變量+禁搶占+加計算公式,就實現了,不同cpu的不同進程調用kmap_atomic函數得到的vaddr是不同的。同時這也給我們實現atomic提供了一種思路,實際上atomic一般都會有一個percpu變量。
3、得到vaddr之后,頁表的建立。
我們就以arm為例,來說明一下,arm初始化的時候,會通過create_mapping來創建一些頁表,比如map_lowmem就會調用 create_mapping函數來建立整個低端內存的映射。
同樣地在函數devicemaps_init中會有如下代碼:
?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);
這里有一點需要說明一點,雖然只映射了一個PAGE_SIZE,但是如果內核采用的二級頁表(即存在pgd,pte),而不是一級頁表(只有pgd)的話,會在create_mapping函數中,分配512個pte項(即512個pte指針ptep),遠遠大于KM_TYPE_NR個數。但是如果采用一級頁表的話,這就會有問題,這種情況應該不能用kmap_atomic函數,因為通過TOP_PTE(vaddr)宏是得不到ptep的,因此目前代碼中有如下保護:
?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開始),內核邏輯將這一小段地址空間分成了若干個節(slot),每一節的大小是一個page的大小,可以用來映射一個page。雖然總共可用512個slot,但是只能用KM_TYPE_NR個,有點遺憾!!!
原文地址: http://blog.chinaunix.net/uid-26817832-id-3358944.html
總結
以上是生活随笔為你收集整理的内核 kmap_atomic分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 内核的文件 Cache 管理
- 下一篇: Linux系统中,read文件过程分析