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 {
  • 相关阅读:
    I.MX6 Surfaceflinger 机制
    理解 Android Fragment
    RPi 2B DDNS 动态域名
    RPi 2B IPC webcam server
    理解 Android MVP 开发模式
    I.MX6 system.img unpack repack
    can't set android permissions
    VMware Ubuntu 共享文件夹
    解决oracle数据库连接不上的问题
    perfect-scrollbar示例
  • 原文地址:https://www.cnblogs.com/aspirs/p/13929146.html
Copyright © 2011-2022 走看看