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

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

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

開(kāi)通VIP
linux內存管理源碼分析--頁(yè)框分配器
http://blog.chinaunix.net/uid-26772321-id-5111297.html
2015

分頁(yè)和分段

  先看一幅圖

  也就是我們實(shí)際中編碼時(shí)遇到的內存地址并不是對應于實(shí)際內存上的地址,我們編碼中使用的地址是一個(gè)邏輯地址,會(huì )通過(guò)分段和分頁(yè)這兩個(gè)機制把它轉為物理地址。而由于linux使用的分段機制有限,可以認為,linux下的邏輯地址=線(xiàn)性地址。也就是,我們編碼使用的是線(xiàn)性地址,之后只需要經(jīng)過(guò)一個(gè)分頁(yè)機制就可以把這個(gè)地址轉為物理地址了。所以我們更重要的可能是去說(shuō)明一下linux的分頁(yè)模型。

 

  系統會(huì )將整個(gè)物理內存分為多個(gè)頁(yè)框,每個(gè)頁(yè)框大小一般是4K(硬件允許情況下也可設置為4M),也就是如果我們有1GB的物理內存,系統就會(huì )將這個(gè)物理內存分為262144個(gè)頁(yè)框。當我們提供一個(gè)線(xiàn)性地址時(shí),系統就會(huì )通過(guò)分頁(yè)機制將這個(gè)線(xiàn)性地址轉換為對應于某個(gè)物理頁(yè)中的某個(gè)內存地址。下圖是linux的分頁(yè)模型


  linux采用四級分頁(yè)模型,這四種頁(yè)表是:頁(yè)全局目錄(PGD)、頁(yè)上級目錄(PUD)、頁(yè)中間目錄(PMD)、頁(yè)表(PTE)。這里的所有頁(yè)全局目錄、頁(yè)上級目錄、頁(yè)中間目錄、頁(yè)表,它們的大小都是一個(gè)頁(yè)。linux下各個(gè)硬件上并不一定都是使用四級目錄的,當使用于沒(méi)有啟動(dòng)物理地址擴展的32位系統上時(shí),只使用二級頁(yè)表,linux會(huì )把頁(yè)上級目錄和頁(yè)中間目錄置空。而在啟用了物理地址擴展的32位系統上時(shí),linux使用的是三級頁(yè)表,頁(yè)上級目錄被置空。而在64位系統上,linux根據硬件的情況會(huì )選擇三級頁(yè)表或者四級頁(yè)表。這個(gè)整個(gè)由線(xiàn)性地址轉換到物理地址的過(guò)程,是由CPU自動(dòng)進(jìn)行的。

  每個(gè)進(jìn)程都有它自己的頁(yè)全局目錄,當進(jìn)程運行時(shí),系統會(huì )將該進(jìn)程的頁(yè)全局目錄基地址保存到cr3寄存器中;而當進(jìn)程被換出時(shí),會(huì )將這個(gè)cr3保存的頁(yè)全局目錄地址保存到進(jìn)程描述符中。之后我們還會(huì )介紹一個(gè)cr2寄存器,用于缺頁(yè)異常處理的。當進(jìn)程運行時(shí),它使用的是它自己的一套頁(yè)表,當它通過(guò)系統調用或陷入內核態(tài)時(shí),使用的是內核頁(yè)表,實(shí)際上,對于所有的進(jìn)程頁(yè)表來(lái)說(shuō),它們的線(xiàn)性地址0xC0000000以上所涉及到的頁(yè)表都是主內核頁(yè)全局目錄(保存在init_mm.pgd),它們的內容等于主內核頁(yè)全局目錄的相應表項,這樣就實(shí)現了所有進(jìn)程的進(jìn)程空間相互隔離,但是內核空間相互共享的情況。當某個(gè)進(jìn)程修改了內核頁(yè)表的一些映射情況后,系統只會(huì )相應的修改主內核頁(yè)全局目錄中的表項(只能修改高端內存中非連續內存區的映射),當其他進(jìn)程訪(fǎng)問(wèn)這些線(xiàn)性地址時(shí),會(huì )出現缺頁(yè)異常,然后修改該進(jìn)程的頁(yè)表項重新映射該地址。

  因為說(shuō)到每個(gè)進(jìn)程都有它自己的頁(yè)全局目錄,如果有100個(gè)進(jìn)程,內存中就要保存100個(gè)進(jìn)程的整個(gè)頁(yè)表集,看起來(lái)會(huì )耗費相當多的內存。實(shí)際上,只有進(jìn)程使用到的情況下系統才會(huì )分配給進(jìn)程一條路徑,比如我們要求訪(fǎng)問(wèn)一個(gè)線(xiàn)性地址,但是這個(gè)地址可能對應的頁(yè)上級目錄、頁(yè)中間目錄、頁(yè)表和頁(yè)都不存在的,這時(shí)系統會(huì )產(chǎn)生一個(gè)缺頁(yè)異常,在缺頁(yè)異常處理中再給進(jìn)程的這個(gè)線(xiàn)性地址分配頁(yè)上級目錄、頁(yè)中間目錄、頁(yè)表和頁(yè)所需的物理頁(yè)框。


地址空間

  一個(gè)線(xiàn)性地址經(jīng)過(guò)分頁(yè)機制轉為一個(gè)對應的物理地址,我們稱(chēng)之為映射,比如我們的一個(gè)線(xiàn)性地址0x00000001經(jīng)過(guò)分頁(yè)機制處理后,對應的物理地址可能是0xffffff01。

  在linux系統中分兩個(gè)地址空間,一個(gè)是進(jìn)程地址空間,一個(gè)是內核地址空間。對于每個(gè)進(jìn)程來(lái)說(shuō),他們都有自己的大小為3G的進(jìn)程地址空間,這些進(jìn)程地址空間是相互隔離的,也就是進(jìn)程A的0x00000001線(xiàn)性地址和進(jìn)程B的0x00000001線(xiàn)性地址并不是同一個(gè)地址,進(jìn)程A也不能通過(guò)自己的進(jìn)程空間直接訪(fǎng)問(wèn)進(jìn)程B的進(jìn)程地址空間。而當線(xiàn)性地址大于3G時(shí)(也就是0xC0000000),這里的線(xiàn)性地址屬于內核空間,內核地址空間的大小為1G,地址從0xC0000000到0xFFFFFFFF。在內核地址空間中,內核會(huì )把前896MB的線(xiàn)性地址直接與物理地址的前896MB進(jìn)行映射,也就是說(shuō),內核地址空間的線(xiàn)性地址0xC0000001所對應的物理地址為0x00000001,它們之間相差一個(gè)0xC0000000。

  linux內核會(huì )將物理內存分為3個(gè)管理區,分別是:

  • ZONE_DMA:包含0MB~16MB之間的內存頁(yè)框,可以由老式基于ISA的設備通過(guò)DMA使用,直接映射到內核的地址空間。
  • ZONE_NORMAL:包含16MB~896MB之間的內存頁(yè)框,常規頁(yè)框,直接映射到內核的地址空間。
  • ZONE_HIGHMEM:包含896MB以上的內存頁(yè)框,不進(jìn)行直接映射,可以通過(guò)永久映射和臨時(shí)映射進(jìn)行這部分內存頁(yè)框的訪(fǎng)問(wèn)。

 整個(gè)結構如下圖


  對于ZONE_DMA和ZONE_NORMAL這兩個(gè)管理區,內核地址都是進(jìn)行直接映射,只有ZONE_HIGHMEM管理區系統在默認情況下是不進(jìn)行直接映射的,只有在需要使用的時(shí)候進(jìn)行映射(臨時(shí)映射或者永久映射)。

 

 

結點(diǎn)和管理區描述符

  為了用于NUMA架構,使用了node用來(lái)描述一個(gè)地方的內存。NUMA大致就是使眾多服務(wù)器像單一系統一樣地運行,這樣每臺服務(wù)器都有自己的內存,每臺服務(wù)器的內存就是一個(gè)node。對于我們PC來(lái)說(shuō),一臺PC就是一個(gè)node。node用struct pglist_data結構表示:

  1. /* 內存結點(diǎn)描述符,所有的結點(diǎn)描述符保存在 struct pglist_data *node_data[MAX_NUMNODES]*/
  2. typedef struct pglist_data {
  3.     /* 管理區描述符的數組 */
  4.     struct zone node_zones[MAX_NR_ZONES];
  5.     /* 頁(yè)分配器使用的zonelist數據結構的數組,將所有結點(diǎn)的管理區按一定的關(guān)聯(lián)鏈接成一個(gè)鏈表,分配內存時(shí)會(huì )按照此鏈表的順序進(jìn)行分配 */
  6.     struct zonelist node_zonelists[MAX_ZONELISTS];
  7.     /* 結點(diǎn)中管理區的個(gè)數 */
  8.     int nr_zones;
  9. #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
  10.     /* 結點(diǎn)中頁(yè)描述符的數組,包含了此結點(diǎn)中所有頁(yè)框描述符,實(shí)際分配是是一個(gè)指針數組 */
  11.     struct page *node_mem_map;
  12. #ifdef CONFIG_MEMCG
  13.     /* 用于資源限制機制 */
  14.     struct page_cgroup *node_page_cgroup;
  15. #endif
  16. #endif
  17. #ifndef CONFIG_NO_BOOTMEM
  18.     /* 用在內核初始化階段 */
  19.     struct bootmem_data *bdata;
  20. #endif
  21. #ifdef CONFIG_MEMORY_HOTPLUG
  22.     /* 自旋鎖 */
  23.     spinlock_t node_size_lock;
  24. #endif
  25.     /* 結點(diǎn)中第一個(gè)頁(yè)框的下標,在numa系統中,頁(yè)框會(huì )有兩個(gè)序號,所有頁(yè)框的一個(gè)序號,還有就是在此結點(diǎn)中的一個(gè)序號
  26.      * 比如結點(diǎn)2中的頁(yè)框1,它在結點(diǎn)2中的序號是1,但是在所有頁(yè)框中的序號是1001,這個(gè)變量就是保存這個(gè)結點(diǎn)首頁(yè)框的序號1000,用于方便轉換
  27.      */
  28.     unsigned long node_start_pfn;
  29.     /* 內存結點(diǎn)的大小,不包括洞(以頁(yè)框為單位) */
  30.     unsigned long node_present_pages;
  31.     /* 結點(diǎn)的大小,包括洞(以頁(yè)框為單位) */
  32.     unsigned long node_spanned_pages;
  33.     
  34.     /* 結點(diǎn)標識符 */
  35.     int node_id;
  36.     /* kswaped頁(yè)換出守護進(jìn)程使用的等待隊列 */
  37.     wait_queue_head_t kswapd_wait;
  38.     wait_queue_head_t pfmemalloc_wait;
  39.     /* 指針指向kswapd內核線(xiàn)程的進(jìn)程描述符 */
  40.     struct task_struct *kswapd; /* Protected by
  41.                        mem_hotplug_begin/end() */
  42.     /* kswapd將要創(chuàng )建的空閑塊大小取對數的值 */
  43.     int kswapd_max_order;
  44.     enum zone_type classzone_idx;
  45. #ifdef CONFIG_NUMA_BALANCING
  46.     /* 以下用于NUMA的負載均衡 */
  47.     /* Lock serializing the migrate rate limiting window */
  48.     spinlock_t numabalancing_migrate_lock;

  49.     /* Rate limiting time interval */
  50.     unsigned long numabalancing_migrate_next_window;

  51.     /* Number of pages migrated during the rate limiting time interval */
  52.     unsigned long numabalancing_migrate_nr_pages;
  53. #endif
  54. } pg_data_t;

  系統中所有的結點(diǎn)描述符都保存在node_data這個(gè)數組中。在pg_data_t這個(gè)結點(diǎn)描述符中,node_zones數組中保存了這個(gè)結點(diǎn)中所有的管理區描述符,雖然系統將物理內存分為三個(gè)區,但是在邏輯上,系統分為了四個(gè)管理區,多出的一個(gè)是ZONE_MOVABLE,這個(gè)區是一個(gè)虛擬的管理區,它并沒(méi)有對應于內存的某個(gè)區域,它的主要目的就是為了避免內存碎片化,它的內存要么全部來(lái)自ZONE_HIGHMEM區,要么全部來(lái)自ZONE_NORMAL區。這些我們在后面的初始化函數中將會(huì )看到。

  每個(gè)結點(diǎn)都有一個(gè)內核線(xiàn)程kswapd,它的作用就是將進(jìn)程或內核持有的,但是不常用的頁(yè)交換到磁盤(pán)上,以騰出更多可用內存。

 

  我們再看看管理區描述符:

  1. /* 內存管理區描述符 */
  2. struct zone {
  3.     /* Read-mostly fields */

  4.     /* zone watermarks, access with *_wmark_pages(zone) macros */
  5.     /* 包括pages_min,pages_low,pages_high
  6.      * pages_min: 管理區中保留頁(yè)的數目
  7.      * pages_low: 回收頁(yè)框使用的下界,同時(shí)也被管理區分配器作為閥值使用,一般這個(gè)數字是pages_min的5/4
  8.      * pages_high: 回收頁(yè)框使用的上界,同時(shí)也被管理區分配器作為閥值使用,一般這個(gè)數字是pages_min的3/2
  9.      */
  10.     unsigned long watermark[NR_WMARK];

  11.     /* 指明在處理內存不足的臨界情況下管理區必須保留的頁(yè)框數目,同時(shí)也用于在中斷或臨界區發(fā)出的原子內存分配請求(就是禁止阻塞的內存分配請求) */
  12.     long lowmem_reserve[MAX_NR_ZONES];

  13. #ifdef CONFIG_NUMA
  14.     int node;
  15. #endif

  16.     /*
  17.      * The target ratio of ACTIVE_ANON to INACTIVE_ANON pages on
  18.      * this zone's LRU. Maintained by the pageout code.
  19.      */
  20.     unsigned int inactive_ratio;

  21.     /* 指向此管理區屬于的結點(diǎn) */
  22.     struct pglist_data *zone_pgdat;
  23.     /* 實(shí)現每CPU頁(yè)框高速緩存,里面包含每個(gè)CPU的單頁(yè)框的鏈表 */
  24.     struct per_cpu_pageset __percpu *pageset;

  25.     /*
  26.      * This is a per-zone reserve of pages that should not be
  27.      * considered dirtyable memory.
  28.      */
  29.     unsigned long dirty_balance_reserve;

  30. #ifndef CONFIG_SPARSEMEM
  31.     /*
  32.      * Flags for a pageblock_nr_pages block. See pageblock-flags.h.
  33.      * In SPARSEMEM, this map is stored in struct mem_section
  34.      */
  35.     unsigned long *pageblock_flags;
  36. #endif /* CONFIG_SPARSEMEM */

  37. #ifdef CONFIG_NUMA
  38.     /*
  39.      * zone reclaim becomes active if more unmapped pages exist.
  40.      */
  41.     unsigned long min_unmapped_pages;
  42.     unsigned long min_slab_pages;
  43. #endif /* CONFIG_NUMA */

  44.     /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
  45.     /* 管理區第一個(gè)頁(yè)框下標 */
  46.     unsigned long zone_start_pfn;

  47.     /* 所有正常情況下可用的頁(yè),總頁(yè)數(不包括洞)減去保留的頁(yè)數 */
  48.     unsigned long managed_pages;
  49.     /* 管理區總大小(頁(yè)為單位),包括洞 */
  50.     unsigned long spanned_pages;
  51.     /* 管理區總大小(頁(yè)為單位),不包括洞 */
  52.     unsigned long present_pages;
  53.     /* 指向管理區的傳統名稱(chēng),"DMA" "NORMAL" "HighMem" */
  54.     const char *name;

  55.     /* 對應于伙伴系統中MIGRATE_RESEVE鏈的頁(yè)塊的數量 */
  56.     int nr_migrate_reserve_block;

  57. #ifdef CONFIG_MEMORY_ISOLATION
  58.     /*
  59.      * Number of isolated pageblock. It is used to solve incorrect
  60.      * freepage counting problem due to racy retrieving migratetype
  61.      * of pageblock. Protected by zone->lock.
  62.      */
  63.     /* 在內存隔離中表示隔離的頁(yè)框塊數量 */
  64.     unsigned long nr_isolate_pageblock;
  65. #endif

  66. #ifdef CONFIG_MEMORY_HOTPLUG
  67.     /* see spanned/present_pages for more description */
  68.     seqlock_t span_seqlock;
  69. #endif

  70.     /* 進(jìn)程等待隊列的hash表,這些進(jìn)程在等待管理區中的某頁(yè) */
  71.     wait_queue_head_t *wait_table;
  72.     /* 等待隊列散列表的大小 */
  73.     unsigned long wait_table_hash_nr_entries;
  74.     /* 等待隊列散列表數組大小 */
  75.     unsigned long wait_table_bits;

  76.     ZONE_PADDING(_pad1_)

  77.     /* Write-intensive fields used from the page allocator */
  78.     /* 保護該描述符的自旋鎖 */
  79.     spinlock_t lock;

  80.     /* free areas of different sizes */
  81.     /* 標識出管理區中的空閑頁(yè)框塊,用于伙伴系統 */
  82.     /* MAX_ORDER為11,分別代表包含大小為1,2,4,8,16,32,64,128,256,512,1024個(gè)連續頁(yè)框的鏈表 */
  83.     struct free_area free_area[MAX_ORDER];

  84.     /* zone flags, see below */
  85.     /* 管理區標識 */
  86.     unsigned long flags;

  87.     ZONE_PADDING(_pad2_)

  88.     /* Fields commonly accessed by the page reclaim scanner */
  89.     /* 活動(dòng)及非活動(dòng)鏈表使用的自旋鎖 */
  90.     spinlock_t lru_lock;
  91.     struct lruvec lruvec;

  92.     /* Evictions & activations on the inactive file list */
  93.     atomic_long_t inactive_age;

  94.     /*
  95.      * When free pages are below this point, additional steps are taken
  96.      * when reading the number of free pages to avoid per-cpu counter
  97.      * drift allowing watermarks to be breached
  98.      */
  99.     unsigned long percpu_drift_mark;

  100. #if defined CONFIG_COMPACTION || defined CONFIG_CMA
  101.     /* pfn where compaction free scanner should start */
  102.     unsigned long compact_cached_free_pfn;
  103.     /* pfn where async and sync compaction migration scanner should start */
  104.     unsigned long compact_cached_migrate_pfn[2];
  105. #endif

  106. #ifdef CONFIG_COMPACTION
  107.     /*
  108.      * On compaction failure, 1<<compact_defer_shift compactions
  109.      * are skipped before trying again. The number attempted since
  110.      * last failure is tracked with compact_considered.
  111.      */
  112.     unsigned int compact_considered;
  113.     unsigned int compact_defer_shift;
  114.     int compact_order_failed;
  115. #endif

  116. #if defined CONFIG_COMPACTION || defined CONFIG_CMA
  117.     /* Set to true when the PG_migrate_skip bits should be cleared */
  118.     bool compact_blockskip_flush;
  119. #endif

  120.     ZONE_PADDING(_pad3_)
  121.     /* 管理區的一些統計數據 */
  122.     atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
  123. } ____cacheline_internodealigned_in_smp;

  此管理區描述符中的實(shí)際把所有屬于該管理區的頁(yè)框保存在兩個(gè)地方:struct free_area free_area[MAX_ORDER]和struct per_cpu_pageset __percpu * pageset。free_area是這個(gè)管理區的伙伴系統,而pageset是這個(gè)區的每CPU頁(yè)框高速緩存。對管理區的理解需要結合伙伴系統和每CPU頁(yè)框高速緩存

 

管理區頁(yè)框分配器(管理所有物理內存頁(yè)框)

  ZONE_NORMAL和ZONE_DMA的地址直接映射到了內核地址空間,但是也不代表內核的代碼可以隨心所欲的通過(guò)線(xiàn)性地址直接訪(fǎng)問(wèn)物理地址。內核通過(guò)一個(gè)管理區頁(yè)框分配器管理著(zhù)物理內存上所有的頁(yè)框,在管理區分配器里的核心系統就是伙伴系統和每CPU頁(yè)框高速緩存(不是硬件上的高速緩存,只是名稱(chēng)一樣)。在linux系統中,管理區頁(yè)框分配器管理著(zhù)所有物理內存,無(wú)論你是內核還是進(jìn)程,需要將一些內存占為己有時(shí),都需要請求管理區頁(yè)框分配器,這時(shí)才會(huì )分配給你應該獲得的物理內存頁(yè)框。當你所擁有的頁(yè)框不再使用時(shí),你必須釋放這些頁(yè)框,讓這些頁(yè)框回到管理區頁(yè)框分配器當中。特別的,對于高端內存,即使從管理區頁(yè)框分配器中獲得了相應的頁(yè)框,我們還需要進(jìn)行映射才能夠使用。

  有時(shí)候目標管理區不一定有足夠的頁(yè)框去滿(mǎn)足分配,這時(shí)候系統會(huì )從另外兩個(gè)管理區中獲取要求的頁(yè)框,但這是按照一定規則去執行的,如下:

  • 如果要求從DMA區中獲取,就只能從ZONE_DMA區中獲取。
  • 如果沒(méi)有規定從哪個(gè)區獲取,就按照順序從 ZONE_NORMAL -> ZONE_DMA 獲取。
  • 如果規定從HIGHMEM區獲取,就按照順序從 ZONE_HIGHMEM -> ZONE_NORMAL -> ZONE_DMA 獲取。

  注意系統是不允許在一次分配中從不同的兩個(gè)管理區獲取頁(yè)框的,并且當請求多個(gè)頁(yè)框時(shí),從伙伴系統中分配給目標的頁(yè)框是連續的,并且請求的頁(yè)數必須是2的次方個(gè)數。


  管理區分配器主要做的事情就是將頁(yè)框通過(guò)伙伴系統或者每CPU頁(yè)框高速緩存分配出去,這里涉及到三個(gè)結構,頁(yè)描述符,伙伴系統,每CPU高速緩存。

  我們先說(shuō)說(shuō)頁(yè)描述符,頁(yè)描述符實(shí)際上并不專(zhuān)屬于描述頁(yè)框,它還用于描述一個(gè)SLAB分配器和SLUB分配器,這個(gè)之后再說(shuō),我們先說(shuō)關(guān)于頁(yè)的:

  1. /* 頁(yè)描述符,描述一個(gè)頁(yè)框,也會(huì )用于描述一個(gè)SLAB,相當于同時(shí)是頁(yè)描述符,也是SLAB描述符 */
  2. struct page {
  3.     /* First double word block */
  4.     /* 用于頁(yè)描述符,一組標志(如PG_locked、PG_error),也對頁(yè)框所在的管理區和node進(jìn)行編號 */
  5.     unsigned long flags; /* Atomic flags, some possibly
  6.                      * updated asynchronously */
  7.     union {
  8.         /* 用于頁(yè)描述符,當頁(yè)被插入頁(yè)高速緩存中時(shí)使用,或者當頁(yè)屬于匿名區時(shí)使用 */
  9.         struct address_space *mapping;
  10.         /* 用于SLAB描述符,用于執行第一個(gè)對象的地址 */
  11.         void *s_mem; /* slab first object */
  12.     };


  13.     /* Second double word */
  14.     struct {
  15.         union {
  16.             /* 作為不同的含義被幾種內核成分使用。例如,它在頁(yè)磁盤(pán)映像或匿名區中標識存放在頁(yè)框中的數據的位置,或者它存放一個(gè)換出頁(yè)標識符 */
  17.             pgoff_t index; /* Our offset within mapping. */
  18.             /* 用于SLAB描述符,指向第一個(gè)空閑對象地址 */
  19.             void *freelist;
  20.             /* 當管理區頁(yè)框分配器壓力過(guò)大時(shí),設置這個(gè)標志就確保這個(gè)頁(yè)框專(zhuān)門(mén)用于系統釋放其他頁(yè)框時(shí)使用 */
  21.             bool pfmemalloc;
  22.         };

  23.         union {
  24. #if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
  25.             /* SLUB使用 */
  26.             unsigned long counters;
  27. #else
  28.             /* SLUB使用 */
  29.             unsigned counters;
  30. #endif

  31.             struct {
  32.                 union {
  33.                     /* 頁(yè)框中的頁(yè)表項計數,如果沒(méi)有為-1,如果為PAGE_BUDDY_MAPCOUNT_VALUE(-128),說(shuō)明此頁(yè)及其后的一共2的private次方個(gè)數頁(yè)框處于伙伴系統中,正在使用時(shí)應該是0 */
  34.                     atomic_t _mapcount;

  35.                     struct { /* SLUB使用 */
  36.                         unsigned inuse:16;
  37.                         unsigned objects:15;
  38.                         unsigned frozen:1;
  39.                     };
  40.                     int units; /* SLOB */
  41.                 };
  42.                 /* 頁(yè)框的引用計數,如果為-1,則此頁(yè)框空閑,并可分配給任一進(jìn)程或內核;如果大于或等于0,則說(shuō)明頁(yè)框被分配給了一個(gè)或多個(gè)進(jìn)程,或用于存放內核數據。page_count()返回_count加1的值,也就是該頁(yè)的使用者數目 */
  43.                 atomic_t _count; /* Usage count, see below. */
  44.             };
  45.             /* 用于SLAB描述符 */
  46.             unsigned int active; /* SLAB */
  47.         };
  48.     };


  49.     /* Third double word block */
  50.     union {
  51.         /* 包含到頁(yè)的最近最少使用(LRU)雙向鏈表的指針,用于插入伙伴系統的空閑鏈表中,只有塊中頭頁(yè)框要被插入 */
  52.         struct list_head lru;


  53.         /* SLAB使用 */
  54.         struct { /* slub per cpu partial pages */
  55.             struct page *next; /* Next partial slab */
  56. #ifdef CONFIG_64BIT
  57.             int pages; /* Nr of partial slabs left */
  58.             int pobjects; /* Approximate # of objects */
  59. #else
  60.             short int pages;
  61.             short int pobjects;
  62. #endif
  63.         };

  64.         struct slab *slab_page; /* slab fields */
  65.         struct rcu_head rcu_head;


  66. #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && USE_SPLIT_PMD_PTLOCKS
  67.         pgtable_t pmd_huge_pte; /* protected by page->ptl */
  68. #endif
  69.     };


  70.     /* Remainder is not double word aligned */
  71.     union {
  72.         /* 可用于正在使用頁(yè)的內核成分(例如: 在緩沖頁(yè)的情況下它是一個(gè)緩沖器頭指針,如果頁(yè)是空閑的,則該字段由伙伴系統使用,在給伙伴系統使用時(shí),表明的是塊的2的次方數,只有塊的第一個(gè)頁(yè)框會(huì )使用) */
  73.         unsigned long private;
  74. #if USE_SPLIT_PTE_PTLOCKS
  75. #if ALLOC_SPLIT_PTLOCKS
  76.         spinlock_t *ptl;
  77. #else
  78.         spinlock_t ptl;
  79. #endif
  80. #endif
  81.         /* SLAB描述符使用,指向SLAB的高速緩存 */
  82.         struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */
  83.         struct page *first_page; /* Compound tail pages */
  84.     };

  85. #if defined(WANT_PAGE_VIRTUAL)
  86.     /* 此頁(yè)框第一個(gè)物理地址對應的線(xiàn)性地址,如果是沒(méi)有映射的高端內存的頁(yè)框,則為空 */
  87.     void *virtual;
  88. #endif /* WANT_PAGE_VIRTUAL */
  89. #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS
  90.     unsigned long debug_flags; /* Use atomic bitops on this */
  91. #endif

  92. #ifdef CONFIG_KMEMCHECK
  93.     void *shadow;
  94. #endif

  95. #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
  96.     int _last_cpupid;
  97. #endif
  98. }

  在struct page描述一個(gè)頁(yè)框時(shí),我們比較關(guān)注的成員變量有unsigned long flags、struct list_head lru和atomic_t _count。

  • flags:包含有很多信息,包括此頁(yè)框屬于的node結點(diǎn)號,此頁(yè)框屬于的zone號和此頁(yè)框的屬性。
  • lru:用于將此頁(yè)描述符放入相應的鏈表,比如伙伴系統或者每CPU頁(yè)框高速緩存。
  • _count:代表頁(yè)框的引用計數,-1代表此頁(yè)框空閑,大于0代表此頁(yè)框分配給了多少個(gè)進(jìn)程使用(共享)。

  linux為了防止內存中產(chǎn)生過(guò)多的碎片,一般把頁(yè)的類(lèi)型分為三種:

  • 不可移動(dòng)頁(yè):在內存中有固定位置,不能移動(dòng)到其他地方。內核中使用的頁(yè)大部分是屬于這種類(lèi)型。
  • 可回收頁(yè):不能直接移動(dòng),但可以刪除,頁(yè)中的內容可以從某些源中重新生成。例如,頁(yè)內容是映射到文件數據的頁(yè)就屬于這種類(lèi)型。對于這種類(lèi)型,kswapd內核線(xiàn)程會(huì )根據可回收頁(yè)的訪(fǎng)問(wèn)頻率,周期性地釋放這些頁(yè)。在內存短缺(分配失敗)時(shí),也會(huì )發(fā)起頁(yè)面回收,釋放這些頁(yè)。
  • 可移動(dòng)頁(yè):可隨意移動(dòng),用戶(hù)空間的進(jìn)程使用的頁(yè)就屬于這種類(lèi)型,它們是通過(guò)進(jìn)程頁(yè)表映射的,把這些頁(yè)復制到新位置時(shí),只要更新進(jìn)程頁(yè)表就可以了。一般這些頁(yè)是從高端內存管理區獲取。


伙伴系統

  伙伴系統實(shí)際上是一個(gè)struct free_area的數組,數組長(cháng)度是MAX_ORDER,也就是11,代表著(zhù)每個(gè)數組元素中鏈表上保存的連續頁(yè)框長(cháng)度是2的order次方。free_area[0]中鏈表保存的是長(cháng)度為1的頁(yè)框,free_area[1]中鏈表上保存的是物理上連續的兩個(gè)頁(yè)框的首頁(yè)框鏈表,free_area[2]中鏈表上保存的是物理上連續4個(gè)頁(yè)框的首頁(yè)框鏈表,free_area[10]中鏈表上保存的是物理上連續1024個(gè)頁(yè)框的首頁(yè)框鏈表,所以整個(gè)伙伴系統中將管理區中的頁(yè)框分為連續的1,2,4,8,16,32,64,128,256,512,1024頁(yè)框放入不同鏈表中保存起來(lái)。而因為伙伴系統中每個(gè)鏈表保存的頁(yè)框都是連續的,所以只有第一個(gè)頁(yè)框會(huì )加入到鏈表中,因為有order,也可以知道此頁(yè)框之后的多少個(gè)頁(yè)框是屬于這一小塊連續頁(yè)框的。當需要在普通內存區申請4個(gè)頁(yè)框大小的內存時(shí),系統會(huì )到普通內存管理區的伙伴系統中的free_area[2]中的第一個(gè)鏈表結點(diǎn),這個(gè)結點(diǎn)的頁(yè)框及其之后3個(gè)頁(yè)框都是空閑的,然后把首頁(yè)框返回給申請者。

  1. /* 伙伴系統的一個(gè)塊,描述1,2,4,8,16,32,64,128,256,512或1024個(gè)連續頁(yè)框的塊 */
  2. struct free_area {
  3.     /* 指向這個(gè)塊中所有空閑小塊的第一個(gè)頁(yè)描述符,這些小塊會(huì )按照MIGRATE_TYPES類(lèi)型存放在不同指針里 */
  4.     struct list_head free_list[MIGRATE_TYPES];
  5.     /* 空閑小塊的個(gè)數 */
  6.     unsigned long nr_free;
  7. };
  在伙伴系統中,因為頁(yè)的分類(lèi)關(guān)系,在每種長(cháng)度相同的連續頁(yè)框中又會(huì )分出多個(gè)不同類(lèi)型的鏈表,如下:

  1. enum {
  2.     MIGRATE_UNMOVABLE, /* 不可移動(dòng)頁(yè) */
  3.     MIGRATE_RECLAIMABLE, /* 可回收頁(yè) */
  4.     MIGRATE_MOVABLE, /* 可移動(dòng)頁(yè) */
  5.     MIGRATE_PCPTYPES, /* 用來(lái)表示每CPU頁(yè)框高速緩存的數據結構中的鏈表的可移動(dòng)類(lèi)型數目 */
  6.     MIGRATE_RESERVE = MIGRATE_PCPTYPES,
  7. #ifdef CONFIG_CMA
  8.     MIGRATE_CMA,
  9. #endif
  10. #ifdef CONFIG_MEMORY_ISOLATION
  11.     MIGRATE_ISOLATE, /* 不能從這個(gè)鏈表分配頁(yè)框,因為這個(gè)鏈表專(zhuān)門(mén)用于NUMA結點(diǎn)移動(dòng)物理內存頁(yè),將物理內存頁(yè)移動(dòng)到使用這個(gè)頁(yè)最頻繁的CPU */
  12. #endif
  13.     MIGRATE_TYPES
  14. };
    保存連續2個(gè)頁(yè)框的free_area[2]的結構如下:


  在從伙伴系統中申請頁(yè)框時(shí),有可能會(huì )遇到一種情況,就是當前需求的連續頁(yè)框鏈表上沒(méi)有可用的空閑頁(yè)框,這時(shí)后,伙伴系統會(huì )從下一級獲取一個(gè)連續長(cháng)度的頁(yè)框塊,將其拆分放入這級列表;當然在擁有者釋放連續頁(yè)框時(shí)伙伴系統也會(huì )適當地進(jìn)行連續頁(yè)框的合并,并放入下一級中。比如:我需要申請4個(gè)頁(yè)框,但是長(cháng)度為4個(gè)連續頁(yè)框塊鏈表沒(méi)有空閑的頁(yè)框塊,伙伴系統會(huì )從連續8個(gè)頁(yè)框塊的鏈表獲取一個(gè),并將其拆分為兩個(gè)連續4個(gè)頁(yè)框塊,放入連續4個(gè)頁(yè)框塊的鏈表中。釋放時(shí)道理也一樣,會(huì )檢查釋放的這幾個(gè)頁(yè)框的之前和之后的物理頁(yè)框是否空閑,并且能否組成下一級長(cháng)度的塊。

 

每CPU頁(yè)框高速緩存

  每CPU頁(yè)框高速緩存也是一個(gè)分配器,配合著(zhù)伙伴系統進(jìn)行使用,這個(gè)分配器是專(zhuān)門(mén)用于分配單個(gè)頁(yè)框的,它維護一個(gè)單頁(yè)框的雙向鏈表,為什么需要這個(gè)分配器,因為每個(gè)CPU都有自己的硬件高速緩存,當對一個(gè)頁(yè)進(jìn)行讀取寫(xiě)入時(shí),首先會(huì )把這個(gè)頁(yè)裝入硬件高速緩存,而如果進(jìn)程對這個(gè)處于硬件高速緩存的頁(yè)進(jìn)行操作后立即釋放掉,這個(gè)頁(yè)有可能還保存在硬件高速緩存中,這樣我另一個(gè)進(jìn)程需要請求一個(gè)頁(yè)并立即寫(xiě)入數據的話(huà),分配器將這個(gè)處于硬件高速緩存中的頁(yè)分配給它,系統效率會(huì )大大增加。

  在每CPU頁(yè)框高速緩存中用一個(gè)鏈表來(lái)維護一個(gè)單頁(yè)框的雙向鏈表,每個(gè)CPU都有自己的鏈表(因為每個(gè)CPU有自己的硬件高速緩存),那些比較可能處于硬件高速緩存中的頁(yè)被稱(chēng)為“熱頁(yè)”,比較不可能處于硬件高速緩存中的頁(yè)稱(chēng)為“冷頁(yè)”。其實(shí)系統判斷是否為熱頁(yè)還是冷頁(yè)很簡(jiǎn)單,越最近釋放的頁(yè)就比較可能是熱頁(yè),所以在雙向鏈表中,從鏈表頭插入可能是熱頁(yè)的單頁(yè)框,在鏈表尾插入可能是冷頁(yè)的單頁(yè)框。分配時(shí)熱頁(yè)就從鏈表頭獲取,冷頁(yè)就從鏈表尾獲取。

  在每CPU頁(yè)框高速緩存中也可能會(huì )遇到?jīng)]有空閑的頁(yè)框(被分配完了),這時(shí)候每CPU頁(yè)框高速緩存會(huì )從伙伴系統中拿出頁(yè)框放入每CPU頁(yè)框高速緩存中,相反,如果每CPU頁(yè)框高速緩存中頁(yè)框過(guò)多,也會(huì )將一些頁(yè)框放回伙伴系統。

  在內核中使用struct per_cpu_pageset結構描述一個(gè)每CPU頁(yè)框高速緩存,其中的struct per_cpu_pages是核心結構體,如下:

  1. /* 描述一個(gè)CPU頁(yè)框高速緩存 */
  2. struct per_cpu_pageset {
  3.     /* 高速緩存頁(yè)框結構 */
  4.     struct per_cpu_pages pcp;
  5. #ifdef CONFIG_NUMA
  6.     s8 expire;
  7. #endif
  8. #ifdef CONFIG_SMP
  9.     s8 stat_threshold;
  10.     s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
  11. #endif
  12. };

  13. struct per_cpu_pages {
  14.     /* 當前CPU高速緩存中頁(yè)框個(gè)數 */
  15.     int count; /* number of pages in the list */
  16.     /* 上界,當此CPU高速緩存中頁(yè)框個(gè)數大于high,則會(huì )將batch個(gè)頁(yè)框放回伙伴系統 */
  17.     int high; /* high watermark, emptying needed */
  18.     /* 在高速緩存中將要添加或被刪去的頁(yè)框個(gè)數 */
  19.     int batch; /* chunk size for buddy add/remove */

  20.     /* Lists of pages, one per migrate type stored on the pcp-lists */
  21.     /* 頁(yè)框的鏈表,如果需要冷高速緩存,從鏈表尾開(kāi)始獲取頁(yè)框,如果需要熱高速緩存,從鏈表頭開(kāi)始獲取頁(yè)框 */
  22.     struct list_head lists[MIGRATE_PCPTYPES];
  23. };

結尾

  下篇再說(shuō)slab了,內容太多。到這里,記住對于物理內存來(lái)說(shuō),系統都是以頁(yè)框作為最小的分配單位,而分配時(shí)必定是要通過(guò)管理區分配器進(jìn)行分配的,在管理區分配器中又必定是通過(guò)伙伴系統或每CPU頁(yè)框分配器進(jìn)行分配的,而我們編程使用到的malloc或者內核中使用的分配小額內存的情況,是使用slab實(shí)現的,slab的作用就是將一個(gè)頁(yè)框細分為多個(gè)小塊內存。









本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
slab分配器簡(jiǎn)明分析
Linux內存管理(一)
linux物理內存描述
13. 內存管理
Linux學(xué)習筆記
Linux slab
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

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