应用场景

在某些系统资源受限场景下,我们需要控制系统内存占用率,腾出足够内存空间以保障用户进程能流畅运行。如果常规的定制化剪裁和进程优化无法满足预期需求时,我们可以启用zRAM内存压缩来达到降低系统内存的目的。

zRAM工作原理

zRam是一个内核模块,提供zRam设备创建及IO接口、压缩算法、压缩解压缩操作接口等功能,其通过在内存中开辟一块自带压缩功能的空间来代替传统的磁盘SWAP空间(只有当zRam设备被填满时,才会发生磁盘换页),达到当系统内存不足时,提高系统性能的目的。
zRam作为一种内存压缩机制是内核内存管理进行内存回收的一种可选方案,为了更好的理解zRam的工作方式和作用,先有必要了解一下内核的内存回收策略。

img


如上图所示,在内存管理进行内存回收时有三个临界点,我们称之为水线,其值从高到低依次为high,low,min,内核进行内存回收的策略可以概括如下:

  • 空闲内存下降,当free < low时,内核唤醒kswapd进程开始内存回收。

  • 空闲内存下降,当free < min时,启动Direct Reclaim机制,所有内存请求须等待内存回收完成后才能得到满足。

  • 空闲内存上升,当free > high时,内存回收进程kswapd进入休眠。

内核是如何确定上面的三个水线值的呢?

static void __setup_per_zone_wmarks(void)
{
    unsigned long pages_min = min_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) {
        if (!is_highmem(zone))
            lowmem_pages += zone_managed_pages(zone);
    }

    for_each_zone(zone) {
        u64 tmp;

        spin_lock_irqsave(&zone->lock, flags);
        tmp = (u64)pages_min * zone_managed_pages(zone);
        do_div(tmp, lowmem_pages);
        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 async page reclaim, and so should
             * not be capped for highmem.
             */
            unsigned long min_pages;

            min_pages = zone_managed_pages(zone) / 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] = tmp;
        }

        /*
         * Set the kswapd watermarks distance according to the
         * scale factor in proportion to available memory, but
         * ensure a minimum size on small systems.
         */
        tmp = max_t(u64, tmp >> 2,
                mult_frac(zone_managed_pages(zone),
                      watermark_scale_factor, 10000));

        zone->watermark_boost = 0;
        zone->_watermark[WMARK_LOW]  = min_wmark_pages(zone) + tmp;
        zone->_watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2;

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

    /* update totalreserve_pages */
    calculate_totalreserve_pages();
}

不难得出:

watermark[WMARK_MIN] = min_free_kbytes>>(PAGE_SHIFT-10) * zone.pages/zone.allpages
temp = max(1/4watermark[WMARK_MIN], zone.pages * watermark_scale_factor / 10000)
watermark[WMARK_LOW] = watermark[WMARK_MIN] + 1/4temp
watermark[WMARK_HIGH] = 3/2*watermark[WMARK_MIN] + 1/2temp

其中的min_free_kbytes和watermark_scale_factor是内核的水位控制参数,除了这两个最直接控制参数,如果要对zram开启时系统性能进行调优,还有一些重要的内核参数需要关注:

  • extra_free_kbytes: 可以理解这个参数指定的是额外加在min与low水线之间的一块free内存,用于避免在可能发生的爆发式内存申请时出现OOM,如果你的系统设计为允许100M的burst allocation出现,就可以把extra_free_kbytes配为100M。

  • watermark_boost_factor: 在内存紧张时,内核将水位线向下调整的幅度。 它将改变kswapd触发频率,进而影响性能。

  • swappiness: 用于控制内核回收内存时匿名页的优先级(1-200,100表示与文件页优先级相同,默认为60),优先级低有利于缓解内存回收时的系统IO读写压力。

  • extfrag_threshold: 内核认为内存碎片过多的阈值。空闲内存块是按大小排序的,因而根据找到的内存块索引值来决定是否要进行内存规整还是直接进行内存回收。

  • page-cluster: 控制从swap空间换入数据时预读页数的参数,是2的指数值。

zRAM适配

  1. 内核配置,打开zRam有关配置项:

    CONFIG_ZRAM=y
    CONFIG_ZRAM_DEBUG=y
    CONFIG_ZRAM_LZ4_COMPRESS=y
    
  2. zRam系统参数配置

    img

  3. 结果验证

    img

img


通过free命令我们看到,系统总内存为1945M,空闲内存1483M,使用的内存为462M,swap分区大小为511M,其中113M已使用,与我们上面的配置基本相符

img

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐