成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux內存管理--高端內存映射與非連續(xù)內存分配

系統(tǒng) Linux
返回頁框線性地址的頁分配函數(shù)對于高端內存是無效的,因為高端內存不會自動的映射到某個線性地址。內核可以采用三種方式來使用高端物理內存:永久內核映射,臨時內核映射和非連續(xù)內存分配。

對于32位的機器來說,高于896的物理內存在內核中屬于高端內存,并沒有對內存做一一的映射,系統(tǒng)保留了128M的線性地址空間來臨時映射這些高于896M的高端物理內存,該線性地址為3G+768m~4G。返回頁框線性地址的頁分配函數(shù)對于高端內存是無效的,因為高端內存不會自動的映射到某個線性地址。例如__get_free_pages(GFP_HIGH_MEM,0)函數(shù)分配高端內存頁框時,返回的是NULL;內核可以采用三種方式來使用高端物理內存:***內核映射,臨時內核映射和非連續(xù)內存分配。建立***內核映射可能會阻塞當前進程的執(zhí)行,這發(fā)生在沒有高端內存沒有空閑的頁表項來做映射的情況下,因此在中斷等不能阻塞的代碼中不要使用***內核映射。臨時內核映射不會發(fā)生阻塞的情況,但必須保證沒有其他的內核路徑在使用同樣的臨時內核映射。


一、***內存映射

***內核映射使用的是內核主頁表中的一個專門的頁表,其地址存放在pkmap_page_table中,頁表的頁表項由宏LAST_PKMAP產生,頁表中包含512或者1024項。

該頁表映射的線性地址從PKMAP_BASE開始,pkmap_count數(shù)組包含了LAST_PKMAP個計數(shù)器,pkmap_page_table頁表中的每項都有對應一個計數(shù)值:

計數(shù)器為0:對應的頁表項是空閑可用的。

計數(shù)器為1:對應的頁表項沒有映射任何高端內存,但是它不能夠使用,因為自從***一次使用以來,其相應的TLB尚未被刷新。

計數(shù)器為n:有多個內核成分使用該頁表項所對應的頁框。

源碼分析:

  1. void fastcall *kmap_high(struct page *page) 
  2. unsigned long vaddr; 
  3.  
  4.  
  5. spin_lock(&kmap_lock); 
  6. //page->virtual記錄了頁框對應的線性地址 
  7. vaddr = (unsigned long)page_address(page); 
  8. //若頁框未被映射過,分配新的空閑頁表項 
  9. if (!vaddr) 
  10. vaddr = map_new_virtual(page); 
  11. //若是剛分配到了空閑頁表項的話,在map_new_virtual()中其count 
  12. //值被設置為了1,在這里再次++ 
  13. pkmap_count[PKMAP_NR(vaddr)]++; 
  14. BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2); 
  15. spin_unlock(&kmap_lock); 
  16. return (void*) vaddr; 
  17. static inline unsigned long map_new_virtual(struct page *page) 
  18. unsigned long vaddr; 
  19. int count; 
  20.  
  21.  
  22. start: 
  23. count = LAST_PKMAP; 
  24. //尋找一個空的頁表項 
  25. for (;;) { 
  26. //從上一次找到的空閑頁表項的位置開始尋找 
  27. last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK; 
  28. if (!last_pkmap_nr) { 
  29. flush_all_zero_pkmaps(); 
  30. count = LAST_PKMAP; 
  31. //找到一個未用的空閑頁表項 
  32. if (!pkmap_count[last_pkmap_nr]) 
  33. break;  /* Found a usable entry */ 
  34. //count變?yōu)?的話,意味著當前沒有空閑的頁表項 
  35. if (--count) 
  36. continue
  37. //沒有找到空閑的頁表項,將當前進程加入到等待隊列,進行調度,直到 
  38. //有空閑的頁表項或者該頁面被別人映射 
  39. DECLARE_WAITQUEUE(wait, current); 
  40.  
  41.  
  42. __set_current_state(TASK_UNINTERRUPTIBLE); 
  43. add_wait_queue(&pkmap_map_wait, &wait); 
  44. spin_unlock(&kmap_lock); 
  45. schedule(); 
  46. remove_wait_queue(&pkmap_map_wait, &wait); 
  47. spin_lock(&kmap_lock); 
  48. //有可能在該進程睡眠期間,有其它進程對該頁面做了內存映射 
  49. if (page_address(page)) 
  50. return (unsigned long)page_address(page); 
  51.  
  52.  
  53. /* Re-start */ 
  54. goto start; 
  55. //得到對應頁表項對應的線性地址 
  56. vaddr = PKMAP_ADDR(last_pkmap_nr); 
  57. //設置對應的頁表項 
  58. set_pte_at(&init_mm, vaddr, 
  59.   &(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot)); 
  60. //設置***內存映射數(shù)組的值 
  61. pkmap_count[last_pkmap_nr] = 1; 
  62. //將page->virtual的值設為vaddr,ok 
  63. set_page_address(page, (void *)vaddr); 
  64.  
  65.  
  66. return vaddr; 

二、臨時內核映射

臨時內核映射比較簡單,在內核中,為每個cpu都保存了一組頁表項,每個頁表項由一個特定的內核成分使用,需要注意的是,不同的內核控制路徑不應該同時使用一個頁表項,這樣的話,會使后一個內核控制路徑將前一個內核控制路徑設置頁表項給沖掉。

建立臨時內核映射使用kmap_atomic()函數(shù)。

  1. void *__kmap_atomic(struct page *page, enum km_type type) 
  2. enum fixed_addresses idx; 
  3. unsigned long vaddr; 
  4.  
  5.  
  6. //禁止內核搶占,以預防不同內核控制路徑使用同一頁表項 
  7. inc_preempt_count(); 
  8. //非高端內存,不用進行高端內存映射 
  9. if (!PageHighMem(page)) 
  10. return page_address(page); 
  11. //得到使用的頁表項的下表索引 
  12. idx = type + KM_TYPE_NR*smp_processor_id(); 
  13. //得到相關頁表項的線性地址 
  14. vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); 
  15. //設置對應的頁表項 
  16. set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); 
  17. local_flush_tlb_one((unsigned long)vaddr); 
  18.  
  19.  
  20. return (void*) vaddr; 

三、非連續(xù)內存分配

下圖顯示了如何使用高于0xc0000000線性地址的線性地址空間:

  1. 內存區(qū)的開始部分包含的是對前896MB的RAM進行映射的線性地址,直接映射的物理內存的末尾的線性地址保存在high_memory變量中。
  2. 內存區(qū)的結尾位置包含的是固定映射的線性地址。
  3. 從PKMAP_BASE開始,是用于高端內存***映射的線性地址。
  4. 其余的線性地址用于非連續(xù)內存區(qū),在物理內存映射和***個內存區(qū)間有一個8M的安全區(qū),用于捕捉對內存的越界訪問,同樣道理,插入其它4KB大小的內存區(qū)來隔離非連續(xù)內存區(qū)。

非連續(xù)內存區(qū)描述符數(shù)據(jù)結構:

  1. struct vm_struct { 
  2. void     *addr;//內存區(qū)***個內存單元的線性地址 
  3. unsigned long    size;//內存區(qū)的大小加上4K,4K是用來檢查越界的內存 
  4. unsigned long     flags;//非連續(xù)內存的類型,VM_ALLOC表示使用vmalloc分配的內存,VM_MAP表示使用vmap分配的內存, 
  5.      //VM_IOREMAP表示用ioremap()分配的內存 
  6. struct page  **pages;//非連續(xù)內存的的物理頁數(shù)組 
  7. unsigned int     nr_pages;//非連續(xù)內存的物理頁的個數(shù) 
  8. unsigned long    phys_addr; 
  9. struct vm_struct    *next;//用來將各個非連續(xù)內存描述符串聯(lián)起來 
  10. }; 

1、分配非連續(xù)的內存區(qū)

分配函數(shù)主要是vmalloc(),vmap(),vmalloc()會去調用__vmalloc_node()函數(shù):

  1. void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, 
  2. int node) 
  3. struct vm_struct *area; 
  4. //size要對其為4K的整數(shù)倍,因為非連續(xù)內存區(qū)域是將各個物理頁進行映射 
  5. size = PAGE_ALIGN(size); 
  6. if (!size || (size >> PAGE_SHIFT) > num_physpages) 
  7. return NULL; 
  8. //找到一塊空閑的線性地址區(qū)域,用來映射該非連續(xù)內存 
  9. area = get_vm_area_node(size, VM_ALLOC, node); 
  10. if (!area) 
  11. return NULL; 
  12.  
  13.  
  14. return __vmalloc_area_node(area, gfp_mask, prot, node); 
  15.  
  16.  
  17. void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, 
  18. pgprot_t prot, int node) 
  19. struct page **pages; 
  20. unsigned int nr_pages, array_size, i; 
  21. //計算要映射的物理頁數(shù) 
  22. nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT; 
  23. //計算vm_struct中pages數(shù)組的數(shù)組元素個數(shù) 
  24. array_size = (nr_pages * sizeof(struct page *)); 
  25. //記錄下物理頁面的數(shù)目 
  26. area->nr_pages = nr_pages; 
  27. //為vm_struct中的pages數(shù)組分配內存 
  28. if (array_size > PAGE_SIZE) { 
  29. pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node); 
  30. area->flags |= VM_VPAGES; 
  31. else 
  32. pages = kmalloc_node(array_size, (gfp_mask & ~__GFP_HIGHMEM), node); 
  33. area->pages = pages; 
  34. if (!area->pages) { 
  35. remove_vm_area(area->addr); 
  36. kfree(area); 
  37. return NULL; 
  38. memset(area->pages, 0, array_size); 
  39. //為非連續(xù)內存進行頁面的分配,每次分配一個頁面,將其頁框指針記錄在pages數(shù)組中 
  40. for (i = 0; i < area->nr_pages; i++) { 
  41. if (node < 0) 
  42. area->pages[i] = alloc_page(gfp_mask); 
  43. else 
  44. area->pages[i] = alloc_pages_node(node, gfp_mask, 0); 
  45. if (unlikely(!area->pages[i])) { 
  46. /* Successfully allocated i pages, free them in __vunmap() */ 
  47. area->nr_pages = i; 
  48. goto fail; 
  49. //將各個物理頁框映射到分配好的空閑線性區(qū)里面去 
  50. if (map_vm_area(area, prot, &pages)) 
  51. goto fail; 
  52. return area->addr; 
  53.  
  54.  
  55. fail: 
  56. vfree(area->addr); 
  57. return NULL; 

__vmalloc_node()并不觸及當前進程的頁表,因此當內核態(tài)進程訪問非連續(xù)內存區(qū)時,會發(fā)生缺頁異常,因為對應的進程的相應地址對應的頁表項為空。當缺頁異常發(fā)生時,異常處理程序會到內核主頁表(init_mm.pgd頁全局目錄)中去查看是否有對應的頁表項,有的話,就會修改當前進程的頁表項,并繼續(xù)進程的執(zhí)行。

2、釋放非連續(xù)的內存區(qū)

  1. void vfree(void *addr) 
  2. BUG_ON(in_interrupt()); 
  3. __vunmap(addr, 1); 
  4. void __vunmap(void *addr, int deallocate_pages) 
  5. struct vm_struct *area; 
  6.  
  7.  
  8. if (!addr) 
  9. return
  10. //釋放的地址應該是4k的整數(shù)倍 
  11. if ((PAGE_SIZE-1) & (unsigned long)addr) { 
  12. printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr); 
  13. WARN_ON(1); 
  14. return
  15. //移除對應的vm_area數(shù)據(jù)描述符,解除對各個物理頁面的頁面映射項 
  16. area = remove_vm_area(addr); 
  17. if (unlikely(!area)) { 
  18. printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n"
  19. addr); 
  20. WARN_ON(1); 
  21. return
  22.  
  23.  
  24. debug_check_no_locks_freed(addr, area->size); 
  25. //需要向伙伴系統(tǒng)歸還非連續(xù)的物理頁 
  26. if (deallocate_pages) { 
  27. int i; 
  28. //將各個物理頁面歸還給伙伴系統(tǒng) 
  29. for (i = 0; i < area->nr_pages; i++) { 
  30. BUG_ON(!area->pages[i]); 
  31. __free_page(area->pages[i]); 
  32.  
  33.  
  34. if (area->flags & VM_VPAGES) 
  35. vfree(area->pages); 
  36. else 
  37. kfree(area->pages); 
  38.  
  39.  
  40. kfree(area); 
  41. return

與vmalloc()一樣,該函數(shù)修改的是主內核頁全局目錄和它的頁表表項,內核永遠不會回收頁全局,頁上級,頁中間目錄,也不會回收頁表,而進程的頁表會指向這些表項。這樣的話,假設一個內核進程訪問已經(jīng)釋放的非連續(xù)內存,最終就會訪問到已經(jīng)被清空的頁表表項,從而引發(fā)缺頁異常,這就是一個錯誤。

責任編輯:奔跑的冰淇淋 來源: ChinaUnix博客
相關推薦

2009-10-19 09:45:06

linux內存存管理

2023-10-18 13:31:00

Linux內存

2018-05-18 09:07:43

Linux內核內存

2013-10-12 11:15:09

Linux運維內存管理

2013-10-11 17:32:18

Linux運維內存管理

2011-12-20 10:43:21

Java

2009-06-03 15:52:34

堆內存棧內存Java內存分配

2025-06-09 04:00:00

2021-07-14 10:00:32

Python內存測量

2018-07-23 09:26:08

iOS內存優(yōu)化

2021-10-15 08:51:09

Linux內存 Kmalloc

2009-06-16 11:11:07

Java內存管理Java內存泄漏

2024-11-07 09:37:46

2021-04-27 13:56:49

內存.映射地址

2023-09-05 09:36:19

2022-03-07 10:54:34

內存Linux

2013-03-28 09:55:37

Java對象

2017-05-18 16:30:29

Linux內存管理

2022-08-08 08:31:00

Linux內存管理

2025-01-06 08:00:09

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av看片| 欧美二区乱c黑人 | 欧美亚洲国产精品 | 日韩精品久久一区 | 中文在线视频观看 | 日韩毛片免费看 | 精品在线观看一区 | 看毛片网站 | 中文字幕一区二区三区四区五区 | 亚洲精选久久 | 99免费看| 懂色中文一区二区三区在线视频 | 97精品视频在线观看 | 美女拍拍拍网站 | 久久精品国产一区二区三区 | 蜜月aⅴ免费一区二区三区 99re在线视频 | 久久激情视频 | 欧美淫片 | 免费特级黄毛片 | 成人网在线观看 | 成年人免费看的视频 | 中文字幕日韩欧美一区二区三区 | 国产一级视屏 | 国产1区在线 | 国产乱人伦精品一区二区 | 超碰在线播 | 天天天操| 国产精品久久国产精品久久 | 国产精品高清一区二区三区 | 在线欧美 | 射欧美 | 91久久婷婷 | 91影院在线观看 | 国产日韩精品视频 | 一级免费看 | 人人干视频在线 | 久久久国产一区 | 青娱乐av | 高清一区二区视频 | 久草视| 中文字字幕在线中文乱码范文 |