週六早上讀核感悟

2021-08-25 01:30:34 字數 2971 閱讀 2364

今天週六,因為老婆還要早起備課,我也就跟著早早起來了,九點半就起床了,閒來無事就看了一會**,懶得寫**看**總可以了吧,主要看了兩塊,都是關於記憶體的,乙個是核心高階臨時對映,乙個是夥伴系統的per_cpu_pages中的冷熱頁面佇列,乙個乙個說。

關於高階對映的原理我就不多說了,核心**很清晰,基本就是在虛擬記憶體的高階為每個cpu都預留了一片區域:

enum fixed_addresses {

#ifdef config_highmem

fix_kmap_end = fix_kmap_begin+(km_type_nr*nr_cpus)-1, //為每乙個cpu都留一片區域

__end_of_fixed_addresses

enum km_type {

km_bounce_read,

km_skb_sunrpc_data,

km_skb_data_softirq,

km_user0, //操作使用者資料時用

km_user1,

km_bio_src_irq,

km_bio_dst_irq,

km_pte0,

km_pte1,

km_irq0, //中斷用

km_irq1,

km_softirq0, //軟中斷用

km_softirq1,

km_type_nr //型別的數量

static void __init kmap_init(void)

unsigned long kmap_vstart;

kmap_vstart = __fix_to_virt(fix_kmap_begin);

kmap_pte = kmap_get_fixmap_pte(kmap_vstart);

kmap_prot = page_kernel;

void *kmap_atomic(struct page *page, enum km_type type)

enum fixed_addresses idx;

unsigned long vaddr;

inc_preempt_count();

if (!pagehighmem(page))

return page_address(page);

idx = type + km_type_nr*smp_processor_id(); //找到屬於本cpu的那一片臨時對映區域

vaddr = __fix_to_virt(fix_kmap_begin + idx);

set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));

__flush_tlb_one(vaddr);

return (void*) vaddr;

上面的過程簡單而又清晰,在核心**中很多地方用到了km_user0/1的地方,說不能在中斷上下文呼叫,這到底是為什麼?而且在上面的**可以看出來,對映的時候,根本不管這個位址是否已經有了對映,而是直接對映進去,那麼可想而知,如果原來有對映的話,那麼資料肯定就被破壞了,其實也不是破壞,而是訪問者的頁面被默默更換了,這可能會釀成事故,我們一般要求虛擬位址是頁面獨佔的,也就是說乙個虛擬位址必須對應僅乙個頁面,除了這個臨時對映,別的對映都可以保證這一點,對於使用者對映,如果不是因為缺頁,那是不會對映新頁面的,因此問題解決,對於核心一一對映,初始化時就對映好了,以後也不會改變,對於vmalloc動態對映,vm_struct鍊錶可以保證乙個位址如果已經被對映就不再會有別的對映,對於kmap對映的永久對映,last_pkmap_nr也會保證這一點,但是對於高階臨時對映卻沒有任何保證,這該如何是好,這難道是核心的不周嗎?如果這樣想的話就錯了,前面幾種對映是***,但是這種保證的代價就是沒有話你就必須等,對於不允許等待的操作或者快速操作,有必要提供乙個特殊通道給它們用,就好比大型超市結賬的地方都有快速區,你如果就買一瓶純淨水,那麼就不必排隊了,核心也是這樣。快速通道就是專門為一些快速操作提供的,但是核心是不允許出錯的,為了防止意外,比如不能保證沒有兩個快速操作同時需要對映,那麼就要進行排隊,而排隊就意味著等待,這好像又回到了前面的那些對映方式,這似乎是乙個怪圈,如果按照這種線性的思路考慮的話,就不可能找到一種截然不同的又不用等待又不會出錯的解決方案,那麼核心可以講排隊的縱向過程鋪開成橫向的多個視窗,也就有了上面的臨時對映的設計方式,km_type中的就是這些視窗,使用者當然可以隨便使用,但是出了問題核心概不負責,因此把規則交給使用者自覺遵守而不是靠核心來扶正,這就是最好的解決方案,這個方案不用等待,來了乙個使用者頁面的對映,那麼由於乙個cpu乙個時間只能有乙個使用者程序陷入核心,那麼為每個cpu都提供乙個km_type視窗的話所有問題就解決了,臨時對映使用者頁面的時候,核心執行緒十分了解自己的行為,它將該頁面對映到自己cpu的km_user0,或者km_user1,然後就可以安心的操作了,因為核心固定了只有操作使用者頁面的才可以對映到這兩個視窗,這個程序之所以安心是因為它相信別的執行緒會遵守這個規定的,另外對它本身的限制就是它的操作必須快速而且不許睡眠,當然核心也不能搶占,這是靠遞增搶占標誌做到的。km_type視窗中的每乙個都有自己的用途,使用者只要按照規定使用就沒有問題,這個特殊的快速通道確實起到了快速操作的作用。

關於夥伴系統的per_cpu_pages中的冷熱頁面佇列前面我就研究過一段時間,如果是程序的頁面釋放,那麼就釋放到熱佇列中,如果是裝置的頁面,那麼就釋放到冷佇列中,因為裝置使用的僅僅是頁面,比如dma,它是不經過cpu的,因此頁面資料就不會被cpu的cache快取,其實很多硬體外設沒有mmu,因此要求為硬體外設分配記憶體的時候必須為它指定實體地址,並且位址必須連續,把它想成實模式cpu就可以了。外設不被cpu快取,那麼外設的頁面置入冷佇列,這樣在釋放頁面的時候,夥伴系統的熱佇列頁面數量肯定比全部數量少,這樣當有頁面分配需要的時候,如果是cpu需要頁面,那麼直接從熱佇列分配,此時很可能資料還在cpu快取中,效率會大大提高,即使你不用老頁面的資料而是使用乙個零頁面,那麼在memset(addr,0,page_size)的時候也會提高效率,因為cpu操作的是虛擬位址,匯流排上才是實體地址。頁面總是順序排列的,不同的是它的虛擬位址可能每次都不同,然而cpu最終訪問的頁面,此時已經過了mmu,cpu是以實體地址操作cache的而不是虛擬位址,因此快取熱頁面才有意義。

週六早上讀核感悟

今天週六,因為老婆還要早起備課,我也就跟著早早起來了,九點半就起床了,閒來無事就看了一會 懶得寫 看 總可以了吧,主要看了兩塊,都是關於記憶體的,乙個是核心高階臨時對映,乙個是夥伴系統的per cpu pages中的冷熱頁面佇列,乙個乙個說。關於高階對映的原理我就不多說了,核心 很清晰,基本就是在虛...