快樂蝦http://blog.csdn.net/lights_joy/lights@hb165.com 本文適用於ADI bf561 DSPuclinux-2008r1-rc8 (移植到vdsp5)Visual DSP++ 5.0 歡迎轉載,但請保留作者資訊
1.1 buddy演算法buddy演算法是用來做記憶體管理的經典演算法,目的是為瞭解決記憶體的外片段。避免外片段的方法有兩種:1,利用分頁單元把一組非連續的空閑頁框映射到非連續的線性地址區間。2,開發適當的技術來記錄現存的空閑連續頁框塊的情況,以盡量避免為滿足對小塊的請求而把大塊的空閑塊進行分割。核心選擇第二種避免方法。buddy演算法將所有空閑頁框分組為11個塊鏈表,每個塊鏈表的每個塊元素分別包含1,2,4,8,16,32,64,128,256,512,1024個連續的頁框,每個塊的第一個頁框的物理地址是該塊大小的整數倍。如,大小為16個頁框的塊,其起始地址是16*2^12(一個頁框的大小為4k,16個頁框的大小為16*4K,1k=1024=2的10次方,4k=2的12次方)的倍數。例,假設要請求一個128個頁框的塊,演算法先檢查128個頁框的鏈表是否有空閑塊,如果沒有則查256個頁框的鏈表,有則將256個頁框的塊分裂兩份,一份使用,一份插入128個頁框的鏈表。如果還沒有,就查512個頁框的鏈表,有的話就分裂為128,128,256,一個128使用,剩餘兩個插入對應鏈表。如果在512還沒查到,則返回出錯訊號。回收過程相反,核心試圖把大小為b的空閑夥伴合并為一個大小為2b的單獨快,滿足以下條件的兩個塊稱為夥伴:1,兩個塊具有相同的大小,記做b;2,它們的物理地址是連續的,3,第一個塊的第一個頁框的物理地址是2*b*2^12的倍數,該演算法迭代,如果成功合并所釋放的塊,會試圖合并2b的塊來形成更大的塊。
1.1.1 記憶體回收buddy演算法的記憶體回收由__free_page和free_page兩個宏及兩個回收函數完成。其定義為:#define __free_page(page) __free_pages((page), 0)#define free_page(addr) free_pages((addr),0)extern void FASTCALL(__free_pages(struct page *page, unsigned int order));extern void FASTCALL(free_pages(unsigned long addr, unsigned int order));
1.1.1.1 __free_pages與free_pages這兩個函數的實現為: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); }}fastcall void free_pages(unsigned long addr, unsigned int order){ if (addr != 0) { VM_BUG_ON(!virt_addr_valid((void *)addr)); __free_pages(virt_to_page((void *)addr), order); }}從這兩個函數的實現可以看出,它們只是接收的參數不同而已,最後的處理方法則是一樣的。free_pages函數接收一個絕對位址做為參數,而__free_pages函數則直接接收page這個結構體的指標做為參數。這其中put_page_testzero函數用於將page->_count減一併判斷其是否為0,如果為0則說明此頁已經沒有使用者使用,此時就釋放這個頁。從這裡還可以看出,當order為0時,此函數試圖先將記憶體頁放到所謂的熱快取中,否則就將其回收到指定階數的鏈表中。
1.1.1.2 __free_pages_ok這個函數位於mm/page_alloc.c:static void __free_pages_ok(struct page *page, unsigned int order){ unsigned long flags; int i; int reserved = 0; // 判斷是否應該釋放這些連續的頁,通常reserved為0 for (i = 0 ; i < (1 << order) ; ++i) reserved += free_pages_check(page + i); if (reserved) return; if (!PageHighMem(page)) // 恒為true debug_check_no_locks_freed(page_address(page),PAGE_SIZE<<order); // 空語句 arch_free_page(page, order); // 空語句 kernel_map_pages(page, 1 << order, 0); // 空語句 local_irq_save(flags); __count_vm_events(PGFREE, 1 << order); // 空語句 free_one_page(page_zone(page), page, order); local_irq_restore(flags);}很簡單,就是調用free_one_page函數進行釋放工作。
1.1.1.3 free_one_pagestatic void free_one_page(struct zone *zone, struct page *page, int order){ spin_lock(&zone->lock); zone->all_unreclaimable = 0; zone->pages_scanned = 0; __free_one_page(page, zone, order); spin_unlock(&zone->lock);}簡單調用__free_one_page函數,繼續跟蹤。
1.1.1.4 __free_one_page/* * Freeing function for a buddy system allocator. * * The concept of a buddy system is to maintain direct-mapped table * (containing bit values) for memory blocks of various "orders". * The bottom level table contains the map for the smallest allocatable * units of memory (here, pages), and each level above it describes * pairs of units from the levels below, hence, "buddies". * At a high level, all that happens here is marking the table entry * at the bottom level available, and propagating the changes upward * as necessary, plus some accounting needed to play nicely with other * parts of the VM system. * At each level, we keep a list of pages, which are heads of continuous * free pages of length of (1 << order) and marked with PG_buddy. Page's * order is recorded in page_private(page) field. * So when we are allocating or freeing one, we can derive the state of the * other. That is, if we allocate a small block, and both were * free, the remainder of the region must be split into blocks. * If a block is freed, and its buddy is also free, then this * triggers coalescing into a block of larger size. * * -- wli */ static inline void __free_one_page(struct page *page, struct zone *zone, unsigned int order){ unsigned long page_idx; int order_size = 1 << order; if (unlikely(PageCompound(page))) destroy_compound_page(page, order); page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1); VM_BUG_ON(page_idx & (order_size - 1)); VM_BUG_ON(bad_range(zone, page)); __mod_zone_page_state(zone, NR_FREE_PAGES, order_size); while (order < MAX_ORDER-1) { unsigned long combined_idx; struct free_area *area; struct page *buddy; buddy = __page_find_buddy(page, page_idx, order); if (!page_is_buddy(page, buddy, order)) break; /* Move the buddy up one level. */ list_del(&buddy->lru); area = zone->free_area + order; area->nr_free--; rmv_page_order(buddy); combined_idx = __find_combined_index(page_idx, order); page = page + (combined_idx - page_idx); page_idx = combined_idx; order++; } set_page_order(page, order); list_add(&page->lru, &zone->free_area[order].free_list); zone->free_area[order].nr_free++;}這個注釋中已經很清楚地說明了buddy演算法的核心,它就是將記憶體分成不同大小的塊,每個塊都由數量不等的頁組成。最小的塊只有一個頁,其次是2個頁、4個頁、8個頁…組成的塊。最多由1024個頁組成。在上述函數中,while迴圈儘可能地將頁面放在更大的塊中,在尋找到最大的塊後,將這個記憶體塊放到相應的塊鏈表中。
1.1.2 尋找buddy頁在buddy演算法中,每一頁都有一個buddy頁與之相對應,使用__page_find_buddy函數可以找到指定頁面對應的buddy頁面:/* * Locate the struct page for both the matching buddy in our * pair (buddy1) and the combined O(n+1) page they form (page). * * 1) Any buddy B1 will have an order O twin B2 which satisfies * the following equation: * B2 = B1 ^ (1 << O) * For example, if the starting buddy (buddy2) is #8 its order * 1 buddy is #10: * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10 * * 2) Any buddy B will have an order O+1 parent P which * satisfies the following equation: * P = B & ~(1 << O) * * Assumption: *_mem_map is contiguous at least up to MAX_ORDER */static inline struct page *__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order){ unsigned long buddy_idx = page_idx ^ (1 << order); return page + (buddy_idx - page_idx);}
1.1.3 確認兩個頁面是否互為buddy要確認兩個指定的頁面是否互為buddy,可以使用page_is_buddy函數:/* * This function checks whether a page is free && is the buddy * we can do coalesce a page and its buddy if * (a) the buddy is not in a hole && * (b) the buddy is in the buddy system && * (c) a page and its buddy have the same order && * (d) a page and its buddy are in the same zone. * * For recording whether a page is in the buddy system, we use PG_buddy. * Setting, clearing, and testing PG_buddy is serialized by zone->lock. * * For recording page's order, we use page_private(page). */static inline int page_is_buddy(struct page *page, struct page *buddy, int order){ if (!pfn_valid_within(page_to_pfn(buddy))) // 恒為false return 0; if (page_zone_id(page) != page_zone_id(buddy)) return 0; if (PageBuddy(buddy) && page_order(buddy) == order) { BUG_ON(page_count(buddy) != 0); return 1; } return 0;}在這裡PageBuddy用於判斷一個頁面是否帶有PG_buddy標記。注意:在初始化的時候,所有的頁面都只有PG_reserved標記。#define PageBuddy(page) test_bit(PG_buddy, &(page)->flags)