The last time the buddy system algorithm is very efficient, but to allocate a memory page from the buddy system, or to do a lot of work, and sometimes think will feel very tired.
Kernel often have a single page request and release operation during system operation. To further improve performance and to make life easier, kernel uses a cache mechanism:
The Memory zone defines the page frame cache for each CPU. Kernel will allocate several single pages in advance from the buddy system at the appropriate time and place them in these caches. Later kernel to apply for a single page, take one directly from the cache and no longer have to deal with the buddy system.
In fact, the memory zone defines two page frame caches for each CPU. A hot cache, a cold cache. Hot or cold, mostly relative to the CPU's cache.
In general, assigning pages from the hot cache can improve system performance because the content of the page is most likely also saved in the CPU cache.
What's the use of cold cache? The page frame in the cache is typically used in DMA operations. We know that the DMA operation does not involve the CPU, so there is no CPU cache involved, so the page frame used for the DMA operation does not need to be allocated from the hot cache. Assigning a page frame to a DMA from the cold cache helps keep the pages in the hot cache still hot.
Well, let's take a look at how this cache mechanism is implemented.
1. Data structure
In the memory zone descriptor, there is a member variable
struct Zone {... struct per_cpu_pageset pageset[nr_cpus]; ...}
This is the page frame cache that is prepared for each CPU.
struct Per_cpu_pageset {struct per_cpu_pages pcp[2]; /* 0:hot. 1:cold */...} ____CACHELINE_ALIGNED_IN_SMP;
Each CPU is visible with two cache:hot and cold.
struct Per_cpu_pages {int count; /* Number of pages in the list */int high; /* high watermark, emptying needed */int batch; /* Chunk size for buddy Add/remove */struct list_head list; /* The list of pages */};
The structure of each cache is very simple. Kernel is placed in the list in advance from a single page assigned to the buddy system, and the number of pages contained in the list is stored in count.
Each time a single page is requested and released, kernel check the count value: When a single page is requested, the cache is populated if the value of count is found to be 0, and when a single page is released, if the value of count is greater than or equal to high watermark, The cache will be scaled down. The amount of one batch per fill or reduction.
I've talked about how the buddy system algorithm allocates and frees a chunk of a page. So what is the difference between allocating and releasing a page block after Per-cpu page frame cache is added?
2. Assigning a block of pages
Assigning a page block is done by the function Buffered_rmqueue. It uses the __rmqueue we've talked about to request memory page blocks from the buddy system, but when you apply for a single page, it uses the PER-CPU page frame cache.
static struct page *buffered_rmqueue (struct zonelist *zonelist, struct zone *zone, int order, gfp_t gfp_flags) { unsigned long flags; struct page *page; int cold =!! (Gfp_flags & __gfp_cold); int CPU; int migratetype = Allocflags_to_migratetype (gfp_flags);
Whether to use hot cache or cold cache is determined by the __gfp_cold bit. Migratetype is the mechanism used by the buddy system to reduce external fragmentation, ignoring it.
If you are applying for a single page, kernel will use PER-CPU page frame cache. Of course, before taking the page frame from the cache, a check is made, and if the cache is empty, the cache needs to be populated first.
AGAIN:    CPU  = GET_CPU (); if (Likely (order == 0)) { struct per_cpu_pages *pcp;         PCP = &ZONE_PCP (ZONE, CPU)->pcp[cold]; local_irq_save (Flags); if (!pcp->count) { pcp->count = rmqueue_bulk (zone, 0, pcp->batch, &pcp->list, migratetype); if (Unlikely (! Pcp->count)) goto failed; }
The work of padding is done by the function Rmqueue_bulk. This is a very simple function, which is to use __rmqueue to apply a single page from the buddy system and put it into the cache.
If the cache is still empty after the fill, the memory is already very scarce and returns NULL.
page = List_entry (pcp->list.next, struct page, LRU); List_del (&PAGE->LRU); pcp->count--;
If the cache is not empty, a page frame is taken from the cache.
This is the case for requesting a single page. If multiple pages are requested, use __rmqueue to request from the buddy system.
} else {Spin_lock_irqsave (&zone->lock, flags); page = __rmqueue (zone, Order, migratetype); Spin_unlock (&zone->lock); if (!page) goto failed; } ... failed:local_irq_restore (flags); Put_cpu (); return NULL;}
3. Release a block of pages
Releasing a page block is done by the function __free_pages. It mainly uses the __free_one_page we've talked about to put the memory page block back into the buddy system, but when a single page is released, it puts the page back into the PER-CPU page frame cache.
fastcall void __free_pages (struct page *page, unsigned int order) {if (Put_page_testzero (page)) {if (order = = 0 ) free_hot_page (page); else __FREE_PAGES_OK (page, order); }}
The PER-CPU page frame cache deals with function Free_hot_page.
void fastcall free_hot_page (struct page *page) {free_hot_cold_page (page, 0);}
/* * free a 0-order page */static void fastcall free_hot_cold_ Page (struct page *page, int cold) { struct zone *zone = page_zone (page); struct per_cpu_pages *pcp; unsigned long flags; ...      PCP = &ZONE_PCP (ZONE, GET_CPU ())->pcp[cold]; local_irq_save (flags); __count_vm_event (Pgfree); List_add (&page->lru, &pcp->list); set_page_private (page, get_ Pageblock_migratetype (page)); pcp->count++; if (pcp-> Count >= pcp->high) { free_pages_bulk (zone , pcp->batch, &pcp->list, 0); pcp->count -= pcp->batch; } local_irq_restore (flags); put_cpu ();}
This function logic is very simple, put the page to be released into the cache. Then check the cache size.
If the cache's count value is greater than or equal to high watermark, use the function Free_pages_bulk to reduce the cache. Free_pages_bulk uses __free_one_page to put batch individual pages back into buddy system.
It is interesting to have a small detail when manipulating Per-cpu page frame cache. Taking and putting back a page frame in the cache list is done from the head of the list, creating a LIFO stack. When Free_pages_bulk shrinks the cache, it starts at the tail end of the list, which is much like the idea of LRU. This small detail will try to keep the page frame hot in the cache.
"The Book of songs," There is a cloud, "descend days of rain, Beecher Pethan soil, Hanzi." "Here's Per-cpu page frame cache, which can be said to be a good example of" rainy Day ".
This article is from the "Kernel blogs" blog, so be sure to keep this source http://richardguo.blog.51cto.com/9343720/1669126
Kernel those things. Memory management (4)---a rainy day