歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux高端內存映射(中)

Linux高端內存映射(中)

日期:2017/3/1 10:22:44   编辑:Linux編程

臨時內核映射

臨時內核映射和永久內核映射相比,其最大的特點就是不會阻塞請求映射頁框的進程,因此臨時內核映射請求可以發生在中斷和可延遲函數中。系統中的每個CPU都有自己的臨時內核映射窗口,根據不同的需求,選擇不同的窗口來創建映射,這些窗口都以枚舉類型定義在km_type中

  1. enum km_type {
  2. KMAP_D(0) KM_BOUNCE_READ,
  3. KMAP_D(1) KM_SKB_SUNRPC_DATA,
  4. KMAP_D(2) KM_SKB_DATA_SOFTIRQ,
  5. KMAP_D(3) KM_USER0,
  6. KMAP_D(4) KM_USER1,
  7. KMAP_D(5) KM_BIO_SRC_IRQ,
  8. KMAP_D(6) KM_BIO_DST_IRQ,
  9. KMAP_D(7) KM_PTE0,
  10. KMAP_D(8) KM_PTE1,
  11. KMAP_D(9) KM_IRQ0,
  12. KMAP_D(10) KM_IRQ1,
  13. KMAP_D(11) KM_SOFTIRQ0,
  14. KMAP_D(12) KM_SOFTIRQ1,
  15. KMAP_D(13) KM_SYNC_ICACHE,
  16. KMAP_D(14) KM_SYNC_DCACHE,
  17. /* UML specific, for copy_*_user - used in do_op_one_page */
  18. KMAP_D(15) KM_UML_USERCOPY,
  19. KMAP_D(16) KM_IRQ_PTE,
  20. KMAP_D(17) KM_NMI,
  21. KMAP_D(18) KM_NMI_PTE,
  22. KMAP_D(19) KM_TYPE_NR
  23. };

其中KM_TYPE_NR標志了一個CPU可以擁有多少個頁表項窗口來建立映射。

臨時內核映射的實現也比永久內核映射要簡單,當一個進程申請在某個窗口創建映射,即使這個窗口已經在之前就建立了映射,新的映射也會建立並且覆蓋之前的映射,所以說這種映射機制是臨時的,並且不會阻塞當前進程。

  1. void *kmap_atomic(struct page *page, enum km_type type)
  2. {
  3. return kmap_atomic_prot(page, type, kmap_prot);
  4. }
  1. /*
  2. * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
  3. * no global lock is needed and because the kmap code must perform a global TLB
  4. * invalidation when the kmap pool wraps.
  5. *
  6. * However when holding an atomic kmap it is not legal to sleep, so atomic
  7. * kmaps are appropriate for short, tight code paths only.
  8. */
  9. void *kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot)
  10. {
  11. enum fixed_addresses idx;
  12. unsigned long vaddr;
  13. /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */
  14. /*為了保證函數的原子性,禁止page fault handler*/
  15. pagefault_disable();
  16. if (!PageHighMem(page))/*屬於低端內存則直接返回page的線性地址*/
  17. return page_address(page);
  18. debug_kmap_atomic(type);
  19. /*smp_processor_id()得到CPU的標識號,用KM_TYPE_NR乘以該標識號就得到了該CPU可用窗口的區段,
  20. 再加上type就得到了相應的屬於該CPU的窗口*/
  21. idx = type + KM_TYPE_NR*smp_processor_id();
  22. vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);/*得到對應頁表項的虛擬地址*/
  23. BUG_ON(!pte_none(*(kmap_pte-idx)));
  24. /*將頁表項與page進行關聯,用kmap_pte-idx而不是用kmap_pte+idx,因為固定映射區是逆向生長的,
  25. 也就是說枚舉項越靠前的部分的虛擬地址越靠後*/
  26. set_pte(kmap_pte-idx, mk_pte(page, prot));
  27. return (void *)vaddr;
  28. }

若要手動撤銷當前的臨時內核映射,則可調用kunmap_atomic()函數

  1. void kunmap_atomic(void *kvaddr, enum km_type type)
  2. {
  3. unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
  4. enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
  5. /*
  6. * Force other mappings to Oops if they'll try to access this pte
  7. * without first remap it. Keeping stale mappings around is a bad idea
  8. * also, in case the page changes cacheability attributes or becomes
  9. * a protected page in a hypervisor.
  10. */
  11. /*如果建立了映射則清除頁表項的內容,並且淸刷相應的TLB項*/
  12. if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))
  13. kpte_clear_flush(kmap_pte-idx, vaddr);
  14. else {
  15. #ifdef CONFIG_DEBUG_HIGHMEM
  16. BUG_ON(vaddr < PAGE_OFFSET);
  17. BUG_ON(vaddr >= (unsigned long)high_memory);
  18. #endif
  19. }
  20. pagefault_enable();/*重新使能page fault handler*/
  21. }

相關閱讀:

Linux高端內存映射(上) http://www.linuxidc.com/Linux/2012-05/60627.htm
Linux高端內存映射(中) http://www.linuxidc.com/Linux/2012-05/60628.htm
Linux高端內存映射(中) http://www.linuxidc.com/Linux/2012-05/60902.htm

Copyright © Linux教程網 All Rights Reserved