欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
內核分配不連續頁(yè)

摘自:http://blog.csdn.net/kickxxx/article/details/9322701

 當buddy系統還有大量的連續物理內存時(shí),我們可以通過(guò)__pages_alloc成功分配很大的一塊連續物理內存空間,隨著(zhù)系統運行時(shí)間加長(cháng),buddy系統內很難中找到一塊大的連續物理內存空間,因此__pages_alloc可能會(huì )失敗,即便通過(guò)kswapd進(jìn)行頁(yè)面的回收和交換,buddy仍然不可避免的碎片化


首先我們要明確的是,連續物理內存的分配并不是必要的。對于大部分DMA操作,我們的確需要連續的物理內存;但是對于某些分配內存情況:比如,模塊加載,設備和聲音驅動(dòng)程序中,可以在內核源碼中關(guān)鍵字vmalloc查找,對vmalloc的使用有個(gè)感性認識。


vmalloc把buddy系統內的不連續物理內存,映射到內核中一段連續的地址空間內,因此對于那些無(wú)法直接映射的高端物理內存Highmem來(lái)說(shuō),vmalloc是主要用途之一(另外一個(gè)用途是應用程序的地址映射,之前我一直搞不清它和vmalloc的關(guān)系。實(shí)際二者沒(méi)關(guān)系,只是看起來(lái)很像)。因此vmalloc理應優(yōu)先使用廉價(jià)的Highmem內存,而把寶貴的低端內存,留給其他的內核操作。事實(shí)上也是如此,vmalloc實(shí)現函數的分配標志,指明了從Highmem分配

  1. void *vmalloc(unsigned long size)  
  2. {  
  3.         return __vmalloc(size, GFP_KERNEL | <strong>__GFP_HIGHMEM</strong>, PAGE_KERNEL);  
  4. }  

對于vmalloc來(lái)說(shuō)是需要預留一定的地址空間的,我個(gè)人覺(jué)得地址空間也算是一種資源,尤其對于IA32體系結構和大部分32bit體系結構,整個(gè)內核地址空間只有1G bytes(3:1 split)。而DMA和Normal內存zone 又需要占用數百M的地址空間,參見(jiàn)下面這個(gè)經(jīng)典的kernel地址空間劃分圖


Persistent mappings和Fixmaps地址空間都比較小,這里我們忽略它們,這樣只剩下直接地址映射和VMALLOC區,這個(gè)劃分應該是平衡兩個(gè)需求的結果

1. 盡量增加DMA和Normal區大小,也就是直接映射地址空間大小,當前主流平臺的內存,基本上都超過(guò)了512MB,很多都是標配1GB內存,因此注定有一部分內存無(wú)法進(jìn)行線(xiàn)性映射。

2. 保留一定數量的VMALLOC大小,這個(gè)值是應用平臺特定的,如果應用平臺某個(gè)驅動(dòng)需要用vmalloc分配很大的地址空間,那么最好通過(guò)在kernel參數中指定vmalloc大小的方法,預留較多的vmalloc地址空間。

3. 并不是Highmem沒(méi)有或者越少越好,這個(gè)是我的個(gè)人理解,理由如下:高端內存就像個(gè)垃圾桶和緩沖區,防止來(lái)自用戶(hù)空間或者vmalloc的映射破壞Normal zone和DMA zone的連續性,使得它們碎片化。當這個(gè)垃圾桶較大時(shí),那么污染Normal 和DMA的機會(huì )自然就小了。


下面的圖是VMALLOC地址空間內部劃分情況



在直接地址映射和VMALLOC區之間有一個(gè)8MiB的隔離帶,隔離帶是做什么的呢? 隔離帶是用來(lái)針對內核故障的保護措施,當訪(fǎng)問(wèn)虛擬地址越界時(shí),則會(huì )產(chǎn)生一個(gè)page fault異常,也就是說(shuō)這個(gè)內核地址空間沒(méi)有對應相應的物理地址,這在內核地址空間是不允許的。如果不存在隔離帶,那么越界訪(fǎng)問(wèn)不知不覺(jué)的跨越直接映射和VMALLOC區,內核卻沒(méi)注意到這個(gè)錯誤。


在VMALLOC內部,會(huì )劃分為多個(gè)vmalloc_area,每個(gè)vmalloc_area直間有一個(gè)4KB的地址空隙,通過(guò)這個(gè)小的隔離,可以防止不同映射區直接的越界訪(fǎng)問(wèn)。


數據結構

在進(jìn)入vmalloc代碼實(shí)現之前,我們先了解相關(guān)的數據結構。

  1. struct vm_struct {  
  2.         /* keep next,addr,size together to speedup lookups */  
  3.         struct vm_struct        *next;  
  4.         void                    *addr;  
  5.         unsigned long           size;  
  6.         unsigned long           flags;  
  7.         struct page             **pages;  
  8.         unsigned int            nr_pages;  
  9.         unsigned long           phys_addr;  
  10. };  

內核在管理虛擬內存地址空間時(shí),必須通過(guò)數據結構來(lái)跟蹤哪些子區域被使用,哪些是空閑的。所有的這些數據連接到一個(gè)鏈表中

@next:所有的vm_struct通過(guò)next 組成一個(gè)單鏈表,表頭為全局變量vmlist

@addr:定義了這個(gè)虛擬地址空間子區域的起始地址

@size:定義了這個(gè)虛擬地址空間子區域的大小

@flags:存儲了與該內存區關(guān)聯(lián)的標志

@pages是一個(gè)指針,指向page指針的數組,每個(gè)數組成員都表示一個(gè)映射到這個(gè)地址空間的物理頁(yè)面的實(shí)例。

@nr_pages:page指針數據的長(cháng)度

@phys_addr:僅當用ioremap映射了由物理地址描述的物理內存區域才有效。


注意 vm_struct和vm_area_struct是完全不同的,雖然二者都是做虛擬地址空間映射的:

1. 前者是內核虛擬地址空間映射,而后者則是應用進(jìn)程虛擬地址空間映射。

2. 前者不會(huì )產(chǎn)生page fault,而后者一般不會(huì )提前分配頁(yè)面,只有當訪(fǎng)問(wèn)的時(shí)候,產(chǎn)生page fault來(lái)分配頁(yè)面。


vmalloc 映射示例

下圖給除了vmalloc映射的一個(gè)實(shí)例,這個(gè)vmalloc區映射了三個(gè)物理內存頁(yè)面


從VMALLOC_START+100開(kāi)始,大小為3*PAGE_SIZE的內核地址空間,被映射到物理頁(yè)面725, 1023和7311


vmalloc 代碼實(shí)現

因為大部分體系結構都支持mmu,這里我們只考慮有mmu的情況。實(shí)際上沒(méi)有mmu支持時(shí),vmalloc就無(wú)法實(shí)現非連續物理地址到連續內核地址空間的映射,vmalloc退化為kmalloc實(shí)現

  1. 506 void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)  
  2. 507 {  
  3. 508         return __vmalloc_node(size, gfp_mask, prot, -1);  
  4. 509 }  
  5. 510 EXPORT_SYMBOL(__vmalloc);  
  6.   
  7.   
  8. 512 /**  
  9. 513  *      vmalloc  -  allocate virtually contiguous memory  
  10. 514  *      @size:          allocation size  
  11. 515  *      Allocate enough pages to cover @size from the page level  
  12. 516  *      allocator and map them into contiguous kernel virtual space.  
  13. 517  *  
  14. 518  *      For tight control over page level allocator and protection flags  
  15. 519  *      use __vmalloc() instead.  
  16. 520  */  
  17. 521 void *vmalloc(unsigned long size)  
  18. 522 {  
  19. 523         return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);  
  20. 524 }  
  21. 525 EXPORT_SYMBOL(vmalloc);  

非常清楚,vmalloc優(yōu)先使用HIGHMEM內存。返回值為內核虛擬地址空間地址,這個(gè)地址以及@size決定的分配空間,一定在VMALLOC范圍之內。

__vmalloc也僅僅是__vmalloc_node包裝函數


  1. 479 /**  
  2. 480  *      __vmalloc_node  -  allocate virtually contiguous memory  
  3. 481  *      @size:          allocation size  
  4. 482  *      @gfp_mask:      flags for the page level allocator  
  5. 483  *      @prot:          protection mask for the allocated pages  
  6. 484  *      @node:          node to use for allocation or -1  
  7. 485  *  
  8. 486  *      Allocate enough pages to cover @size from the page level  
  9. 487  *      allocator with @gfp_mask flags.  Map them into contiguous  
  10. 488  *      kernel virtual space, using a pagetable protection of @prot.  
  11. 489  */  
  12. 490 static void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot,  
  13. 491                             int node)  
  14. 492 {  
  15. 493         struct vm_struct *area;  
  16. 494   
  17. 495         size = PAGE_ALIGN(size);  
  18. 496         if (!size || (size >> PAGE_SHIFT) > num_physpages)  
  19. 497                 return NULL;  
  20. 498   
  21. 499         area = get_vm_area_node(size, VM_ALLOC, node, gfp_mask);  
  22. 500         if (!area)  
  23. 501                 return NULL;  
  24. 502   
  25. 503         return __vmalloc_area_node(area, gfp_mask, prot, node);  
  26. 504 }  

495 把請求的@size按照頁(yè)面對齊,說(shuō)明分配是按照4K對齊的

非常自然的,分配過(guò)程分為兩個(gè)步驟:1 分配地址空間,2 進(jìn)行映射

499 從VMALLOC地址空間申請一塊合適的地址空間

503 有了地址空間后,就需要對地址空間進(jìn)行頁(yè)面映射,也就是說(shuō)分配頁(yè)面物理頁(yè)面


分配地址空間

  1. 263 struct vm_struct *get_vm_area_node(unsigned long size, unsigned long flags,  
  2. 264                                    int node, gfp_t gfp_mask)  
  3. 265 {  
  4. 266         return __get_vm_area_node(size, flags, VMALLOC_START, VMALLOC_END, node,  
  5. 267                                   gfp_mask);  
  6. 268 }  

在VMALLOC_START和VMALLOC_END指定的范圍內查找


  1. 169 static struct vm_struct *__get_vm_area_node(unsigned long size, unsigned long flags,  
  2. 170                                             unsigned long start, unsigned long end,  
  3. 171                                             int node, gfp_t gfp_mask)  
  4. 172 {  
  5. 173         struct vm_struct **p, *tmp, *area;  
  6. 174         unsigned long align = 1;  
  7. 175         unsigned long addr;  
  8. 176   
  9. 177         BUG_ON(in_interrupt());  
  10. 178         if (flags & VM_IOREMAP) {  
  11. 179                 int bit = fls(size);  
  12. 180   
  13. 181                 if (bit > IOREMAP_MAX_ORDER)  
  14. 182                         bit = IOREMAP_MAX_ORDER;  
  15. 183                 else if (bit < PAGE_SHIFT)  
  16. 184                         bit = PAGE_SHIFT;  
  17. 185   
  18. 186                 align = 1ul << bit;  
  19. 187         }  
  20. 188         addr = ALIGN(start, align);  
  21. 189         size = PAGE_ALIGN(size);  
  22. 190         if (unlikely(!size))  
  23. 191                 return NULL;  
  24. 192   
  25. 193         area = kmalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);  
  26. 194   
  27. 195         if (unlikely(!area))  
  28. 196                 return NULL;  
  29. 197   
  30. 198         /*  
  31. 199          * We always allocate a guard page.  
  32. 200          */  
  33. 201         size += PAGE_SIZE;  
  34. 202   
  35. 203         write_lock(&vmlist_lock);  
  36. 204         for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {  
  37. 205                 if ((unsigned long)tmp->addr < addr) {  
  38. 206                         if((unsigned long)tmp->addr + tmp->size >= addr)  
  39. 207                                 addr = ALIGN(tmp->size +  
  40. 208                                              (unsigned long)tmp->addr, align);  
  41. 209                         continue;  
  42. 210                 }  
  43. 211                 if ((size + addr) < addr)  
  44. 212                         goto out;  
  45. 213                 if (size + addr <= (unsigned long)tmp->addr)  
  46. 214                         goto found;  
  47. 215                 addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);  
  48. 216                 if (addr > end - size)  
  49. 217                         goto out;  
  50. 218         }  
  51. 219   
  52. 220 found:  
  53. 221         area->next = *p;  
  54. 222         *p = area;  
  55. 223   
  56. 224         area->flags = flags;  
  57. 225         area->addr = (void *)addr;  
  58. 226         area->size = size;  
  59. 227         area->pages = NULL;  
  60. 228         area->nr_pages = 0;  
  61. 229         area->phys_addr = 0;  
  62. 230         write_unlock(&vmlist_lock);  
  63. 231   
  64. 232         return area;  
  65. 233   
  66. 234 out:  
  67. 235         write_unlock(&vmlist_lock);  
  68. 236         kfree(area);  
  69. 237         if (printk_ratelimit())  
  70. 238                 printk(KERN_WARNING "allocation failed: out of vmalloc space - use vmalloc=<size> to increase size.\n");  
  71. 239         return NULL;  
  72. 240 }  

@start是進(jìn)行掃描的首地址,@end是掃描的終止地址。在start和end指定的地址空間內分配。

193 首先分配一個(gè)vm_struct 結構,因為這個(gè)機構很小,自然使用kmalloc進(jìn)行分配了,至于在哪個(gè)node分配,不用care

198~201 每個(gè)vm_struct之間都有4KB的隔離區,所以這里多分配4KB

204 ~ 218 循環(huán)遍歷已經(jīng)創(chuàng )建的vm_struct區,找到能夠創(chuàng )建地址空間的位置

205 ~ 209 如果start大于當前vm_struct的起始位置,那么我們嘗試下一個(gè)。同時(shí)判斷start是否落在這個(gè)vm_struct內,如果是還要修改start

213 ~ 214 如果size + addr小于當前的vm_struct,說(shuō)明匹配了一個(gè)可用位置,直接跳到found標號

221 ~ 222 把這個(gè)vm_struct增加到vmlist中去

從這個(gè)函數我們可以看出來(lái),vm_struct的分配并不會(huì )考慮最優(yōu)匹配,而是在碰到一個(gè)夠用空間后直接返回。


分配物理頁(yè)面 并映射

__vmalloc_area_node

  1. 426 void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,  
  2. 427                                 pgprot_t prot, int node)  
  3. 428 {  
  4. 429         struct page **pages;  
  5. 430         unsigned int nr_pages, array_size, i;  
  6. 431   
  7. 432         nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;  
  8. 433         array_size = (nr_pages * sizeof(struct page *));  
  9. 434   
  10. 435         area->nr_pages = nr_pages;  
  11. 436         /* Please note that the recursion is strictly bounded. */  
  12. 437         if (array_size > PAGE_SIZE) {  
  13. 438                 pages = __vmalloc_node(array_size, gfp_mask | __GFP_ZERO,  
  14. 439                                         PAGE_KERNEL, node);  
  15. 440                 area->flags |= VM_VPAGES;  
  16. 441         } else {  
  17. 442                 pages = kmalloc_node(array_size,  
  18. 443                                 (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,  
  19. 444                                 node);  
  20. 445         }  
  21. 446         area->pages = pages;  
  22. 447         if (!area->pages) {  
  23. 448                 remove_vm_area(area->addr);  
  24. 449                 kfree(area);  
  25. 450                 return NULL;  
  26. 451         }  
  27. 452   
  28. 453         for (i = 0; i < area->nr_pages; i++) {  
  29. 454                 if (node < 0)  
  30. 455                         area->pages[i] = alloc_page(gfp_mask);  
  31. 456                 else  
  32. 457                         area->pages[i] = alloc_pages_node(node, gfp_mask, 0);  
  33. 458                 if (unlikely(!area->pages[i])) {  
  34. 459                         /* Successfully allocated i pages, free them in __vunmap() */  
  35. 460                         area->nr_pages = i;  
  36. 461                         goto fail;  
  37. 462                 }  
  38. 463         }  
  39. 464   
  40. 465         if (map_vm_area(area, prot, &pages))  
  41. 466                 goto fail;  
  42. 467         return area->addr;  
  43. 468   
  44. 469 fail:  
  45. 470         vfree(area->addr);  
  46. 471         return NULL;  
  47. 472 }  

432 根據area的size計算需要的物理頁(yè)面數目,減去1個(gè)PAGE_SIZE是因為這個(gè)vmalloc區包含一個(gè)4KB的隔離區

433 ~ 445 為area->pages數組分配內存,理論上一個(gè)頁(yè)面只能保證1000個(gè)page指針,所以area->pages也使用vmalloc分配就很正常了

453 ~ 463調用alloc_pages_node一個(gè)一個(gè)的分配page,所以vmalloc的分配速度自然沒(méi)有使用alloc_pages的kmalloc高,但是vmalloc的成功率就很高了。

465 目前為止,還有一件事沒(méi)完成,那就是物理地址對邏輯地址的映射,map_vm_area就是做這事的


線(xiàn)性地址到物理地址映射

  1. 148 int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)  
  2. 149 {  
  3. 150         pgd_t *pgd;  
  4. 151         unsigned long next;  
  5. 152         unsigned long addr = (unsigned long) area->addr;  
  6. 153         unsigned long end = addr + area->size - PAGE_SIZE;  
  7. 154         int err;  
  8. 155   
  9. 156         BUG_ON(addr >= end);  
  10. 157         pgd = pgd_offset_k(addr);  
  11. 158         do {  
  12. 159                 next = pgd_addr_end(addr, end);  
  13. 160                 err = vmap_pud_range(pgd, addr, next, prot, pages);  
  14. 161                 if (err)  
  15. 162                         break;  
  16. 163         } while (pgd++, addr = next, addr != end);  
  17. 164         flush_cache_vmap((unsigned long) area->addr, end);  
  18. 165         return err;  
  19. 166 }  
  20. 167 EXPORT_SYMBOL_GPL(map_vm_area);  

153 別忘了,減去隔離區的一個(gè)PAGE_SIZE大小

158 ~ 163 對addr和end范圍內的頁(yè)表進(jìn)行映射,包括pgd pud pmd和pte。

164 這是一個(gè)體系結構相關(guān)的函數,有些體系結構無(wú)法察覺(jué)到頁(yè)表的變化,因此在修改頁(yè)表后,需要程序主動(dòng)的去刷新以下;有些CPU有感知變化的能力,會(huì )自動(dòng)的刷新高速緩存,IA32就是如此。


GOOD,分析完了,vmalloc的代碼還是簡(jiǎn)明清晰的,這次閱讀解決了幾個(gè)以前迷惑的問(wèn)題

1. vmalloc區域是不是和應用空間內存映射一樣,通過(guò)page fault來(lái)裝載頁(yè)面的。

答案:不是,vmalloc映射建立好后,邏輯地址,物理頁(yè)面全部分配好,而且頁(yè)表也已經(jīng)更新好,和用戶(hù)空間映射完全一樣。這是合理的,因為如果像用戶(hù)空間映射那樣,訪(fǎng)問(wèn)地址產(chǎn)生page fault,會(huì )使得vmalloc獲得的內存使用上受到極大的限制,比如不能在禁止調度的地方訪(fǎng)問(wèn)vmalloc分配的地址。




本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Linux vmalloc/vfree函數實(shí)現解讀
圖解線(xiàn)性地址(高端內存部分
ioremap()
Linux內存管理:Vmalloc - 博客 - 伯樂(lè )在線(xiàn)
linux vmalloc
Linux內存管理(二)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久