swap分析及其使用

 

什么是swap

swap主要是在内存不够用的时候,将部分内存上的数据交换到swap空间上,以便让系统不会因为内存不够用而导致oom或者更致命的情况出现。当内存使用存在压力的时候,开始触发内存回收行为,就可能会使用swap空间。

内核将很少使用的部分内存换出到块设备,相当于提供了更多的主内存,这种机制成为页交换(swapping)或者换页(paging),由内核实现,对应用程序是透明的。

如果一个很少使用的页的后备存储器是一个块设备,那么就无需换出被修改的页,而是可以直接与块设备同步。腾出的页帧可以重用,如果再次需要修改数据,可以从来源重新建立该页。

如果页的后备存储器是一个文件,但不能在内存中修改,那么在当前不需要的情况下,可以直接丢弃该页。

以上这三种技术,连同选择很少使用页的策略,统称为页面回收。

为什么要进行内存回收?

1.内核需要为突发到来的内存申请提供足够的内存,所以一般情况下保证有足够的free空间对于内核来说是必要的。
  另外,Linux内核使用cache策略虽然是不用白不用,内核会使用内存中的page cache对部分文件进行缓存,一边提升文件的读写效率。
  所以内核有必要设计一个周期行回收内存机制,以便cache的使用和其他相关内存的使用不至于让系统的剩余内存长期处于很少的状态。

2.当真的有大于空闲内存的申请到来的是偶,会触发强制内存回收。

所以内核在应对这两类回收的需求下,分别实现两种不同的机制:

一个是使用kswapd进程对内存进行周期检查,以保证平常状态下剩余内存尽可能够用。
另一个是页面回收(page reclaim),就是当内存分配时没有空闲内存可以满足要求时,触发直接内存回收。

这两种内存回收的触发路径不同:

kswapd作为一个后台守护进程,会定期检查内存的使用情况,并检测即将发生的内存不足。可使用该守护进程换出页,并释放那些最不常用的。
    参见mm/vmscan.c中的kswapd()主逻辑。

kswapd
    -->balance_pgdat
        -->kswapd_shrink_zone
            -->shrink_zone
                -->shrink_lruvec

如果内存检测到在某个操作期间内存严重不足,将调用try_to_free_pages。该函数检查当前内存域中所有页,并释放最不常用的那些。
    参见内核代码中的mm/page_alloc.c中的__alloc_pages_slowpath方法。

__alloc_pages_nodemask
    -->__alloc_pages_slowpath
        -->__alloc_pages_direct_reclaim  直接页面回收然后分配内存
            -->__perform_reclaim  执行同步直接页面回收
                -->try_to_free_pages 
                    -->do_try_to_free_pages
                        -->shrink_zones
                            -->shrink_zone
                                -->shrink_lruvec

这两个方法实际进行内存回收的过程殊途同归,最终头时调用shrink_zone()方法针对每个zone的内存页缩减。

这个方法中在调用shrink_lruvec()这个方法对每个组织页的链表进行进程检查,找到这个线索之后,我们就可以清晰地看到内存回收操作究竟这对的page有哪些了。

static void shrink_lruvec(struct lruvec *lruvec, int swappiness,
              struct scan_control *sc, unsigned long *lru_pages)
{

    while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
                    nr[LRU_INACTIVE_FILE]) {
        unsigned long nr_anon, nr_file, percentage;
        unsigned long nr_scanned;

        for_each_evictable_lru(lru) {
            if (nr[lru]) {
                nr_to_scan = min(nr[lru], SWAP_CLUSTER_MAX);
                nr[lru] -= nr_to_scan;

                nr_reclaimed += shrink_list(lru, nr_to_scan,
                                lruvec, sc);
            }
        }

}

内存回收主要需要进行少秒的链表有如下4个:

anon的inactive,anon的active,file的inactive,file的active。

就是说内存回收主要针对的就是内存中的文件页(file cache)和匿名页。

关于活跃还是不活跃的判断内核会使用lru算法进行并行处理并进行标记。

#define LRU_BASE 0
#define LRU_ACTIVE 1
#define LRU_FILE 2

enum lru_list {
    LRU_INACTIVE_ANON = LRU_BASE,
    LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
    LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
    LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
    LRU_UNEVICTABLE,
    NR_LRU_LISTS
};

整个扫描的过程分几个循环:
1.首先扫描每个zone删的cgroup组;
2.然后再以cgroup的内存为单元进行page链表的扫描;
3.内核会先扫描anon的active链表,将不频繁的放进inactive链表中,然后扫描inactive链表,将里面活跃的移回active链表中;
4.进行swap的时候,先对inactive的页进行换出;
5.如果是file的文件映射page页,则判断其是否为脏数据,如果是脏数据就写回,不是脏数据可以直接释放。

这样看来,内存回收这个行为会对两种内存的使用进行回收:

一种是anon的匿名页内存,主要回收手段是swap;另一种是file-backed的文件映射页,主要释放手段是写回和清空。

内存对匿名页和文件缓存一共用了四条链表进行组织,回收过程主要是针对这四条链表进行扫描和操作。

kswapd什么时候进行swap操作?

内核的两种内存回收机制kswapd周期检查和直接内存回收,直接内存回收比较好理解,当申请的内存大于剩余内存的时候,就会触发直接回收。

kswapd进程在周期检查的时候触发回收的条件是什么呢?

kswapd进程要周期对内存进行检测,达到一定阈值的时候开始进行内存回收。这个所谓的阈值可以理解为内存目前的使用压力。虽然我们还有剩余内存,但是当剩余内存比较小的时候,就是内存压力较大的时候,就应该开始试图回收写内存了,这样才能保证系统尽可能有足够的内存给突发的内存申请所使用。

基于swappiness调优

详情请参考:http://blog.csdn.net/tenfyguo/article/details/50185915

swappiness是一个swap相关百分比参数,默认值是60,范围是0-100。

This control is used to define how aggressive the kernel will swap memory pages.  Higher values will increase agressiveness, lower values decrease the amount of swap.  A value of 0 instructs the kernel not to initiate swap until the amount of free and file-backed pages is less than the high water mark in a zone.

从上面这段话可知,swappiness表示内核swap内存页的积极程度。值越高越是积极的进行swap;降低表示积极性越低。当swappiness值为0是,不是不进行swap,只是一个zone中内存的free和file-backed页低于高水位标记才会进行swap。

shrink_lruvec()调用get_scan_count()方法,get_scan_count用于决定anon和文件LRU列表被扫描的积极程度。swappiness参数实际上是知道内核在清空内存的时候,是更倾向于清空file-backed内存还是匿名页的swap。

static void get_scan_count(struct lruvec *lruvec, int swappiness,
               struct scan_control *sc, unsigned long *nr,
               unsigned long *lru_pages)
{
    struct zone_reclaim_stat *reclaim_stat = &lruvec->reclaim_stat;
    u64 fraction[2];
    u64 denominator = 0;    /* gcc */
    struct zone *zone = lruvec_zone(lruvec);
    unsigned long anon_prio, file_prio;
    enum scan_balance scan_balance;
    unsigned long anon, file;
    bool force_scan = false;
    unsigned long ap, fp;
    enum lru_list lru;
    bool some_scanned;
    int pass;

    /*
     * If the zone or memcg is small, nr[l] can be 0.  This
     * results in no scanning on this priority and a potential
     * priority drop.  Global direct reclaim can go to the next
     * zone and tends to have no problems. Global kswapd is for
     * zone balancing and it needs to scan a minimum amount. When
     * reclaiming for a memcg, a priority drop can cause high
     * latencies, so it's better to scan a minimum amount there as
     * well.
     */
    if (current_is_kswapd()) {
        if (!zone_reclaimable(zone))
            force_scan = true;
        if (!mem_cgroup_lruvec_online(lruvec))
            force_scan = true;
    }
    if (!global_reclaim(sc))
        force_scan = true;

    /* If we have no swap space, do not bother scanning anon pages. */
    if (!sc->may_swap || (get_nr_swap_pages() <= 0)) {
        scan_balance = SCAN_FILE;
        goto out;
    }

    /*
     * Global reclaim will swap to prevent OOM even with no
     * swappiness, but memcg users want to use this knob to
     * disable swapping for individual groups completely when
     * using the memory controller's swap limit feature would be
     * too expensive.
     */
    if (!global_reclaim(sc) && !swappiness) {
        scan_balance = SCAN_FILE;
        goto out;
    }

    /*
     * Do not apply any pressure balancing cleverness when the
     * system is close to OOM, scan both anon and file equally
     * (unless the swappiness setting disagrees with swapping).
     */
    if (!sc->priority && swappiness) {
        scan_balance = SCAN_EQUAL;
        goto out;
    }

    /*
     * Prevent the reclaimer from falling into the cache trap: as
     * cache pages start out inactive, every cache fault will tip
     * the scan balance towards the file LRU.  And as the file LRU
     * shrinks, so does the window for rotation from references.
     * This means we have a runaway feedback loop where a tiny
     * thrashing file LRU becomes infinitely more attractive than
     * anon pages.  Try to detect this based on file LRU size.
     */
    if (global_reclaim(sc)) {
        unsigned long zonefile;
        unsigned long zonefree;

        zonefree = zone_page_state(zone, NR_FREE_PAGES);
        zonefile = zone_page_state(zone, NR_ACTIVE_FILE) +
               zone_page_state(zone, NR_INACTIVE_FILE);

        if (unlikely(zonefile + zonefree <= high_wmark_pages(zone))) {
            scan_balance = SCAN_ANON;
            goto out;
        }
    }如果触发的是全局回收,并且zonefile+zonefree<=high_wmark_pages(zone)条件成立时,就将scan_banlance这个标记置为SCAN_ANON。后续处理scan_balance的时候,则一定会进行针对匿名页的swap操作。
再来看看zonefile+zonefree<=high_wmark_pages(zone)这个公式就能理解了。zonefile是内存中文件映射的总量,zonefree相当于剩余内存总量。内核一般认为,如果zonefile还有的话,就尽量通过清空文件缓存获得部分内存,而不必只使用swap方式对anon内存进行交换。
这样是为了防止进入cache陷阱。这个判断对系统的影响是,swappiness为0时,有剩余内存的情况下也可能发生交换。

    /*
     * There is enough inactive page cache, do not reclaim
     * anything from the anonymous working set right now.
     */
    if (!inactive_file_is_low(lruvec)) {
        scan_balance = SCAN_FILE;
        goto out;
    }

    scan_balance = SCAN_FRACT;

    /*
     * With swappiness at 100, anonymous and file have the same priority.
     * This scanning priority is essentially the inverse of IO cost.
     */
    anon_prio = swappiness;
    file_prio = 200 - anon_prio;

1.如果swappiness设置为100,那么匿名页和文件将用同样的优先级进行回收。
   很明显,使用清空文件的的方式将有利于减轻内存回收时可能造成的IO压力。因为如果file-backed中的数据不是脏数据的话,那么可以不用写回,这样就没有IO发生,而一旦进行交换,就一定会造成IO。所以系统默认将swappiness的值设置为60,这样回收内存时,对file-backed的文件cache的清空比例会更大,内核更倾向于进行缓存清空而不是交换。

2.如果swappiness值是60,内核回收的时候也不是完全按照60:140比例清空。在计算具体回收大小的时候,还需要参考当前内存使用的其他信息。
   我们在此需要明确的概念是:swappiness的值是用来控制内存回收时,回收的匿名页更多一些还是祸首的file-backed更多一些。

3.swappiness为0的话,也不是根本不进行swap。
   内存真的不够用时,还是会进行swap。比如为global_reclaim的时候,还是会导致在处理scan_balance的时候进行SCAN_ANON。

    /*
     * OK, so we have swap space and a fair amount of page cache
     * pages.  We use the recently rotated / recently scanned
     * ratios to determine how valuable each cache is.
     *
     * Because workloads change over time (and to avoid overflow)
     * we keep these statistics as a floating average, which ends
     * up weighing recent references more than old ones.
     *
     * anon in [0], file in [1]
     */

    anon  = get_lru_size(lruvec, LRU_ACTIVE_ANON) +
        get_lru_size(lruvec, LRU_INACTIVE_ANON);
    file  = get_lru_size(lruvec, LRU_ACTIVE_FILE) +
        get_lru_size(lruvec, LRU_INACTIVE_FILE);

    spin_lock_irq(&zone->lru_lock);
    if (unlikely(reclaim_stat->recent_scanned[0] > anon / 4)) {
        reclaim_stat->recent_scanned[0] /= 2;
        reclaim_stat->recent_rotated[0] /= 2;
    }

    if (unlikely(reclaim_stat->recent_scanned[1] > file / 4)) {
        reclaim_stat->recent_scanned[1] /= 2;
        reclaim_stat->recent_rotated[1] /= 2;
    }

    /*
     * The amount of pressure on anon vs file pages is inversely
     * proportional to the fraction of recently scanned pages on
     * each list that were recently referenced and in active use.
     */
    ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
    ap /= reclaim_stat->recent_rotated[0] + 1;

    fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
    fp /= reclaim_stat->recent_rotated[1] + 1;
    spin_unlock_irq(&zone->lru_lock);

    fraction[0] = ap;
    fraction[1] = fp;
    denominator = ap + fp + 1;
out:
    some_scanned = false;
    /* Only use force_scan on second pass. */
    for (pass = 0; !some_scanned && pass < 2; pass++) {
        *lru_pages = 0;
        for_each_evictable_lru(lru) {
            int file = is_file_lru(lru);
            unsigned long size;
            unsigned long scan;

            size = get_lru_size(lruvec, lru);
            scan = size >> sc->priority;

            if (!scan && pass && force_scan)
                scan = min(size, SWAP_CLUSTER_MAX);

            switch (scan_balance) {
            case SCAN_EQUAL:
                /* Scan lists relative to size */
                break;
            case SCAN_FRACT:
                /*
                 * Scan types proportional to swappiness and
                 * their relative recent reclaim efficiency.
                 */
                scan = div64_u64(scan * fraction[file],
                            denominator);
                break;
            case SCAN_FILE:
            case SCAN_ANON:
                /* Scan one type exclusively */
                if ((scan_balance == SCAN_FILE) != file) {
                    size = 0;
                    scan = 0;
                }
                break;
            default:
                /* Look ma, no brain */
                BUG();
            }

            *lru_pages += size;
            nr[lru] = scan;

            /*
             * Skip the second pass and don't force_scan,
             * if we found something to scan.
             */
            some_scanned |= !!scan;
        }
    }
}

可以使用sysctl vm.swappiness=10对swappiness进行设置。

dirty_ratio

同步刷脏页,会阻塞应用程序。这个参数控制文件系统的同步写缓冲区的大小,单位是百分比,表示当写缓冲区使用到系统内存多少的时候(即指定了当文件系统缓存脏页数量达到系统内存百分比时),开始向磁盘写出数据,及系统不得不开始处理缓存脏页,在此过程中很多应用程序可能会因为系统转而处理文件I/O而阻塞变慢。

增大会使系统内存更多用于磁盘写缓冲,也可以极大提高系统的写性能。

dirty_background_ratio

异步刷脏页,不会阻塞应用程序。这个参数控制文件系统的后台进程,在合适刷新磁盘。当写缓冲使用到系统内存一定百分比的时候,就会触发pdflush/flush/kdmflush等后台回写进程运行,将一定缓存的脏页异步地输入外存。

一般dirty_ratio比dirty_background_radio要大,先达到dirty_background_ratio的条件然后触发flush进程进行异步的回写操作,但是这一过程应用进程仍然可以进行写操作,如果多个应用程序写入的量大于刷出的量那自然会达到dirty_ratio的标准,此时操作系统会进入同步刷出脏页的过程,阻塞应用进程。

增大会使更多系统内存用于磁盘写缓冲。

dirty_expire_centisecs

申明Linux内核写缓冲区里面的数据多旧了之后,pdflush进程就开始考虑写到磁盘中去,单位是1/100秒。缺省是3000,即20秒的缓存就会被刷新到磁盘。如果太小,刷新磁盘就会比较频繁,导致I/O占用太多时间。如过太大,在某些异常情况会造成数据丢失,同时暂用太多内存。

dirty_writeback_centisecs

这个参数控制内核额脏数据树新进程pdflush的运行间隔,单位是1/100秒,缺省是500,即5秒。

vfs_cache_pressure

设置了虚拟内存回收directory和inode缓存的倾向,这个值越大,越倾向于回收。

缺省值为100;低于100,将导致内核更倾向于保留directory和inode cache;高于100,将导致内核倾向于回收directory和inode cache。

以上这些数据还是要根据系统实际内存以及不同的工作场景进行调优。

内存水位标记(watermark)

Linux为内存的使用设置了三种内存水位标记:high、low、min。
high:剩余的内存在high以上表示内存剩余较多,目前内存使用压力不大。
high-low:表示剩余内存存在一定压力。
low-min:表示内存开始使用有较大压力,剩余内存不多了。
min:是最小水位标记,当剩余内存达到这个状态时,就说明内存面临很大压力。小于min这部分内存,内核是保留给特定情况下使用的,一般不分配。

内存回收行为就是基于剩余内存的水位标记进行的:

当系统内存低于watermark[low]的时候,内核的kswapd开始起作用,进行内存回收。直到剩余内存达到watermark[high]的时候停止。

如果内存消耗导致达到了或者低于watermark[min]时,就会触发直接回收(direct reclaim)。

min_free_kbytes

内存的watermark标记是根据当前内存总大小和min_free_kbytes进行运算的来的。

/proc/sys/vm/min_free_kbytes决定了每个zone的watermark[min]的大小,然后根据min的大小和每个zone内存大小分别算出每个zone的low和high水位标记。

init_per_zone_wmark_min  初始化min_free_kbytes
    -->setup_per_zone_wmarks
        -->__setup_per_zone_wmarks  根据min_free_kbytes计算low和high的值

下面来分析一下low和high水位值是如何计算的:

static void __setup_per_zone_wmarks(void)
{
    unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
    unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10);
    unsigned long lowmem_pages = 0;
    struct zone *zone;
    unsigned long flags;

    /* Calculate total number of !ZONE_HIGHMEM pages */
    for_each_zone(zone) {  针对ZONE_HIGHMEM特殊处理
        if (!is_highmem(zone))
            lowmem_pages += zone->managed_pages;
    }

    for_each_zone(zone) {  遍历所有zone
        u64 min, low;

        spin_lock_irqsave(&zone->lock, flags);
        min = (u64)pages_min * zone->managed_pages;
        do_div(min, lowmem_pages);  针对非HIGHMEM,min即为pages_min。
        low = (u64)pages_low * zone->managed_pages;
        do_div(low, vm_total_pages);  针对非HIGHMEM,low为0.

        if (is_highmem(zone)) {
            /*
             * __GFP_HIGH and PF_MEMALLOC allocations usually don't
             * need highmem pages, so cap pages_min to a small
             * value here.
             *
             * The WMARK_HIGH-WMARK_LOW and (WMARK_LOW-WMARK_MIN)
             * deltas control asynch page reclaim, and so should
             * not be capped for highmem.
             */
            unsigned long min_pages;

            min_pages = zone->managed_pages / 1024;
            min_pages = clamp(min_pages, SWAP_CLUSTER_MAX, 128UL);
            zone->watermark[WMARK_MIN] = min_pages;
        } else {
            /*
             * If it's a lowmem zone, reserve a number of pages
             * proportionate to the zone's size.
             */
            zone->watermark[WMARK_MIN] = min;
        }

        zone->watermark[WMARK_LOW]  = min_wmark_pages(zone) +
                    low + (min >> 2);  low水位线位于min的1.25倍地方
        zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) +
                    low + (min >> 1);  high水位线位于min的1.5倍地方

        __mod_zone_page_state(zone, NR_ALLOC_BATCH,
            high_wmark_pages(zone) - low_wmark_pages(zone) -
            atomic_long_read(&zone->vm_stat[NR_ALLOC_BATCH]));

        spin_unlock_irqrestore(&zone->lock, flags);
    }

    /* update totalreserve_pages */
    calculate_totalreserve_pages();
}

通过cat /proc/zoneinfo可以看到不同zone的watermark:

cat /proc/zoneinfo
Node 0, zone      DMA
  pages free     3840
        min      43
        low      53 = 43+0+(43/4)=53
        high     64 =43+0+(43/2)=64
        scanned  0
        spanned  4095
        present  3996
        managed  3840

Node 0, zone    DMA32
  pages free     176977
        min      8215
        low      10268
        high     12322
        scanned  0
        spanned  1044480
        present  744139
        managed  724056

Node 0, zone   Normal
  pages free     168622
        min      8636
        low      10795
        high     12954
        scanned  0
        spanned  779776
        present  779776
        managed  755387

zone_reclaim_mode

当一个内存区域内部的内存耗尽时,是从其内部进行内存回收还是可以从其他zone进行回收的选项。在申请内存时(get_page_from_freelist()),内核在当前zone内没有足够的内存可用的情况下,会根据zone_reclaim_mode的设置来决策是从下一个zone找空闲内存还是在zone内部进行回收。

0:意味着关闭zone_claim模式,可以从其他zone或NUMA节点回收内存。
1:打开zone_claim模式,这样内存回收只会发生在本地节点内。
2:在本地回收内存时,可以将可以将cache中的脏数据回写硬盘,以回收内存。
3:可以用swap方式回收内存。

不同的参数配置会在NUMA环境中对其他内存节点的内存使用产生不同的影响,大家可以根据自己的情况进行设置以优化你的应用。
默认情况下,zone_reclaim模式是关闭的。这在很多应用场景下可以提高效率,比如文件服务器,或者依赖内存中cache比较多的应用场景。
这样的场景对内存cache速度的依赖要高于进程进程本身对内存速度的依赖,所以我们宁可让内存从其他zone申请使用,也不愿意清本地cache。
如果确定应用场景是内存需求大于缓存,而且尽量要避免内存访问跨越NUMA节点造成的性能下降的话,则可以打开zone_reclaim模式。
此时页分配器会优先回收容易回收的可回收内存(主要是当前不用的page cache页),然后再回收其他内存。
打开本地回收模式的写回可能会引发其他内存节点上的大量的脏数据写回处理。如果一个内存zone已经满了,那么脏数据的写回也会导致进程处理速度收到影响,产生处理瓶颈。
这会降低某个内存节点相关的进程的性能,因为进程不再能够使用其他节点上的内存。但是会增加节点之间的隔离性,其他节点的相关进程运行将不会因为另一个节点上的内存回收导致性能下降。
除非针对本地节点的内存限制策略或者cpuset配置有变化,对swap的限制会有效约束交换只发生在本地内存节点所管理的区域上。

page-cluster

page-cluster是用来控制从swap空间换入数据的时候,一次连续读取的页数这相当于对交换空间的预读取。这里的连续指的是swap空间上的连续,而不是内存地址上的连续。

这个文件中设置的是2的指数。0,则预读取1页;3,则预读取8页。

创建、删除swap分区

查看swap相关信息

swapon –s和cat /proc/swaps的结果是一样的,可以用于查看当前系统的swap分区总结。

Filename                Type        Size    Used    Priority
/dev/sdb1                               partition    7999484    576596    -1

free -m用于显示系统空闲和使用的内存量,其中包括swap信息。

             total       used       free     shared    buffers     cached
Mem:          5794       5590        203        237       1843       1755
-/+ buffers/cache:       1990       3803
Swap:         7811        563       7248

创建swap分区

有两种方式创建swap分区:一种是基于物理分区,另一种是基于一个文件。

基于物理分区创建

  • 创建一个swap分区:fdisk -l /dev/sdb1
  • 格式化分区:mkswap -c v1 /dev/sdb1
  • 修改/etc/fstab文件,增加:

    # /etc/fstab: static file system information.
    #
    # Use 'blkid' to print the universally unique identifier for a
    # device; this may be used with UUID= as a more robust way to name devices
    # that works even if disks are added and removed. See fstab(5).
    #
    # <file system> <mount point>   <type>  <options>       <dump>  <pass>
    # swap was on /dev/sdb1 during installation
    UUID=47b966f2-8157-4650-a2c2-32f0907081bf none            swap    sw              0       0

  • 激活swap分区:swapon –a /dev/sdb1
  • 查看swap分区:swapon -s 或cat /proc/swaps

基于文件创建

  • dd if=/dev/zero of=/home/lubaoquan/swap/tmp.swap bs=1G count=1,创建空文件
  • mkswap tmp.swap,创建为swap文件

Setting up swapspace version 1, size = 1048572 KiB
no label, UUID=f63f7b37-be4c-4794-b8bd-ddd810a42730

  • swapon /tmp/tmp.swap,激活swap分区
  • 修改/etc/fstab文件
/tmp/tmp.swap swap swap default 0 0
  • 查看swap分区信息swapon –s

Filename                Type        Size    Used    Priority
/dev/sdb1               partition    7999484    583536    -1
/tmp/tmp.swap       file        1048572    0    -2

删除swap分区

  • swapoff /dev/sdb1
  • 修改/etc/fstab文件

关于swap的一个测试

关闭打开清空swap

sudo swapoff -a关闭swap,然后free –m查看使用情况

             total       used       free     shared    buffers     cached
Mem:          5794       5131        662        361       1002       1858
-/+ buffers/cache:       2270       3523
Swap:            0          0          0

 

sudo swapon -a再打开,查看free -m:

             total       used       free     shared    buffers     cached
Mem:          5794       5139        654        366       1002       1863
-/+ buffers/cache:       2272       3521
Swap:         7811          0       7811

swap变化

使用eat_mem.c不断分配大内存。

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

int main(int argc, char** argv)

{

int max = -1;

int mb = 0;

    char* buffer;

if(argc > 1)

        max = atoi(argv[1]);

while((buffer=malloc(10*1024*1024)) != NULL && mb != max)

{

        memset(buffer, 0,10*1024*1024);

        mb = mb + 10;

        printf("Allocated %d MB ", mb);

        sleep(1);

}

    return 0;

}

执行./eat_mem,使用watch 'free -m'可以动态查看Mem和Swap的使用情况:

Every 2.0s: free -m                                                                                                             Fri Jan 20 16:41:31 2017

             total       used       free     shared    buffers     cached
Mem:          5794        691       5102        114         19        218
-/+ buffers/cache:        453       5341
Swap:         7811       1229       6582

在eat_mem执行到11710 MB的时候,被Killed了。

./eat_mem
Allocated 10 MB

Allocated 11660 MB
Allocated 11670 MB
Allocated 11680 MB
Allocated 11690 MB
Allocated 11700 MB
Allocated 11710 MB
Killed

在这个过程中可以看到,Mem的free越来越小,Swap的used空间越来越多,越来越多的内容被置换到Swap区域。

在内容开始置换到swap分区过程中,系统的响应速度越来越慢。最后达到swap分区也承受不了,eat_mem就会被Killed。

swap分区的优先级

在使用多个swap分区或者文件的时候,还有一个优先级的概念。

在swapon的时候,可以使用-p指定相关swap空间的优先级,值越大优先级越高,范围是-1到32767。

内核在使用swap空间的时候总是先使用优先级搞得空间,后使用优先级低的。

如果把多个swap空间的优先级设置成一样的,那么swap空间将会以轮询的方式并行使用。

如果两个swap放在不同硬盘上,相同优先级可以起到类似的RAID效果,增大swap的读写效率。

编程时使用mlock()可以将指定的内存标记为不换出。

参考文档

http://blog.csdn.net/cyuyan112233/article/details/18803589

http://blog.csdn.net/tenfyguo/article/details/50185915

http://blog.chinaunix.net/uid-24774106-id-3954137.html

http://chuansong.me/n/361090851159

原文地址:https://www.cnblogs.com/arnoldlu/p/6291251.html