linux kernel swap

 

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.19\include\linux\Swapops.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 {

 

posted @ 2020-11-04 22:33  aspirs  阅读(624)  评论(0编辑  收藏  举报