shrink_page_list(struct list_head *page_list, struct pglist_data *pgdat, struct scan_control *sc, enum ttu_flags ttu_flags, struct reclaim_stat *stat, bool force_reclaim)
初步被选出来的系统认为满足条件的页面放在 page_list
这个链表中,至于如何选择满足条件的页面,是由内存回收算法LRU决定,这是内存管理方面的知识,这里不作详细地描述。在这个函数中会对 page_list
链表中所有的页面逐一处理,若是匿名的并且没有被 swap cache 缓存的页面,通过 add_to_swap
函数通知 swap core 和 swap cache 把该页面的数据交换出内存,随后在 try_to_unmap
函数中修改该 page 对应的 pte
,使其指向 swap entry 的 pte
。因此 add_to_swap
函数则是 swap core 和 swap cache 与内存回收之间的桥梁。
Swap Out - 从内存到磁盘
内核在回收anonymous pages前,会把它们的内容复制到一个swap area的某个slot中保存起来,这个过程叫做swap out,对应的执行函数是add_to_swap()。
首先需要调用get_swap_page()函数从swap area中分配空余的slot,然后增加swap cache(交换缓存)对准备swap out的页面的指向,并标记这个页面的状态为"dirty"。由于swap cache的作用主要体现在swap in的过程中,因此将放在下文详细介绍。
等到调用swap_writepage(),才会执行真正的I/O操作,将页面的内容写入外部的swap area,然后清除swap cache对页面的指向,释放页面所占的内存:
int swap_writepage(struct page *page, struct writeback_control *wbc) { int ret = 0; if (try_to_free_swap(page)) { unlock_page(page); goto out; } if (frontswap_store(page) == 0) { set_page_writeback(page); unlock_page(page); end_page_writeback(page); goto out; } ret = __swap_writepage(page, wbc, end_swap_bio_write); out: return ret; }
reference:
https://blog.csdn.net/qkhhyga2016/article/details/88722458
https://zhuanlan.zhihu.com/p/70964551
good blog:
https://blog.csdn.net/qkhhyga2016/article/details/88722458
swap address_space structure
一个swap space会有多个swap area,一个swap area由一个swap_info_struct来描述。
一个swap area会有许多的slot,一个slot对应一个page。
swp_entry_t由type和offset组成,它是一个long类型的变量,64bit kernel下高7bit为type field;低57bit为offset field
7bit type field,所以最大支持127个type:
4.19includelinuxSwapops.h
#define SWP_TYPE_SHIFT(e) ((sizeof(e.val) * 8) - (MAX_SWAPFILES_SHIFT + RADIX_TREE_EXCEPTIONAL_SHIFT)) //=8*8 - (5+2) = 64-7=57. type bit width is 7, 2^7-1 = 127(support type max: 127) #define SWP_OFFSET_MASK(e) ((1UL << SWP_TYPE_SHIFT(e)) - 1)
/* * Store a type+offset into a swp_entry_t in an arch-independent format */ static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset) { swp_entry_t ret; ret.val = (type << SWP_TYPE_SHIFT(ret)) | (offset & SWP_OFFSET_MASK(ret)); return ret; } static inline unsigned swp_type(swp_entry_t entry) { return (entry.val >> SWP_TYPE_SHIFT(entry)); } /* * Extract the `offset' field from a swp_entry_t. The swp_entry_t is in * arch-independent format */ static inline pgoff_t swp_offset(swp_entry_t entry) { return entry.val & SWP_OFFSET_MASK(entry); }
一个swap_area(一个type)对应许多个struct address_space,一个address_space会管理64M(swap space)空间。
/* One swap address space for each 64M swap space */ #define SWAP_ADDRESS_SPACE_SHIFT 14 #define SWAP_ADDRESS_SPACE_PAGES (1 << SWAP_ADDRESS_SPACE_SHIFT) extern struct address_space *swapper_spaces[]; #define swap_address_space(entry) (&swapper_spaces[swp_type(entry)][swp_offset(entry) >> SWAP_ADDRESS_SPACE_SHIFT])
swap_address_space()根据swp_entry_t确定此swap entry在哪个address_space
swapper_spaces是一个address_space类型的指针数组,每个元素相当于是一个type,里面的每个元素指向一块包含有多个struct address_space的buffer,这块buffer在init_swap_address_space()里alloc:
mm/swap_state.c
int init_swap_address_space(unsigned int type, unsigned long nr_pages) { struct address_space *spaces, *space; unsigned int i, nr; nr = DIV_ROUND_UP(nr_pages, SWAP_ADDRESS_SPACE_PAGES); //根据一共有多少个page,计算需要多少个address_space,一个address_space管理64M:SWAP_ADDRESS_SPACE_PAGES为2^14 spaces = kvcalloc(nr, sizeof(struct address_space), GFP_KERNEL); if (!spaces) return -ENOMEM; for (i = 0; i < nr; i++) { space = spaces + i; xa_init_flags(&space->i_pages, XA_FLAGS_LOCK_IRQ); atomic_set(&space->i_mmap_writable, 0); space->a_ops = &swap_aops; /* swap cache doesn't use writeback related tags */ mapping_set_no_writeback_tags(space); } nr_swapper_spaces[type] = nr; swapper_spaces[type] = spaces; return 0; }
int __add_to_swap_cache(struct page *page, swp_entry_t entry) pgoff_t idx = swp_offset(entry); address_space = swap_address_space(entry); xa_lock_irq(&address_space->i_pages); for (i = 0; i < nr; i++) { //如果CONFIG_TRANSPARENT_HUGEPAGE没开,nr是1 set_page_private(page + i, entry.val + i); error = radix_tree_insert(&address_space->i_pages, //将page插入到i_pages的radix tree上,index为swp_entry_t里的offset idx + i, page + i); if (unlikely(error)) break; } if (likely(!error)) { address_space->nrpages += nr; //nrpages加nr,表示这个address space管理的page num多了nr个 __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, nr); ADD_CACHE_INFO(add_total, nr); } else {