zoukankan      html  css  js  c++  java
  • 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.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 {
  • 相关阅读:
    [LeetCode] 134. Gas Station Java
    [LeetCode] 22. Best Time to Buy and Sell Stock II Java
    [LeetCode] 55. Jump Game Java
    [LeetCode] 264. Ugly Number II Java
    [LeetCode] 331. Verify Preorder Serialization of a Binary Tree Java
    [LeetCode] 232. Implement Queue using Stacks Java
    java.lang.ClassNotFoundException: org.apache.juli.logging.LogFactory的解决办法
    Linux centos 连接网络
    MyEclipse默认编码为GBK,修改为UTF8的方法
    表单
  • 原文地址:https://www.cnblogs.com/aspirs/p/13929146.html
Copyright © 2011-2022 走看看