调参侠必知必会:上(Kernel内存调优参数)

呼呼呼呼,本篇主要讲下如何利用Kernel自带的内存参数进行性能优化。

性能是个很玄乎的存在,同样的配置参数,在服务器上可能健步如飞,在ARM手机上可能烧成火龙果了,高端机可能流畅如丝,低端机却卡出残影...所以一套参数是不能走遍天下的,内存优化的一大主要思路就是对这些参数的调整,以便达到更好的性能优化效果!

先介绍下,Linux内存的调整参数大部分在目录/proc/sys/vm下,他们都在kernel/sysctl.c中定义:

OP4EC1:/proc/sys/vm # ls
admin_reserve_kbytes         dirty_writeback_centisecs  min_free_kbytes           panic_on_oom                user_reserve_kbytes
block_dump                   dirtytime_expire_seconds   mmap_min_addr             percpu_pagelist_fraction    vfs_cache_pressure
compact_memory               drop_caches                mmap_rnd_bits             reap_mem_on_sigkill         vm_swappiness_threshold1
compact_unevictable_allowed  extfrag_threshold          mmap_rnd_compat_bits      stat_interval               vm_swappiness_threshold2
direct_swappiness            extra_free_kbytes          oom_dump_tasks            stat_refresh                want_old_faultaround_pte
dirty_background_bytes       kswapd_threads             oom_kill_allocating_task  swap_ratio                  watermark_boost_factor
dirty_background_ratio       laptop_mode                overcommit_kbytes         swap_ratio_enable           watermark_scale_factor
dirty_bytes                  legacy_va_layout           overcommit_memory         swappiness
dirty_expire_centisecs       lowmem_reserve_ratio       overcommit_ratio          swappiness_threshold1_size
dirty_ratio                  max_map_count              page-cluster              swappiness_threshold2_size

先挑几个常用的重点部分讲一下

min_free_kbytes

内核在每个zone中都预留一部分内存,用来给高优先级进程或者紧急分配。
一般需要分配标志位带有GFP_HIGH、GPF_ATOMIC或者GFP_MEMALLOC才能分配出内存。
我们可以通过cat /proc/zoneinfo查看,单位都是page哦,《奔跑吧Linux内核》书上标的是KB是有问题滴:

 pages free     184710
        min      2705
        low      19113
        high     19789
        spanned  2095616
        present  1980484
        managed  1921719
        protection: (0, 8675, 8677)

三个水位分别是:

  • high 内存回收到该值时停止回收。
  • low 内存到该值时触发kswapd线程的内存回收。
  • min 如果剩余内存减少到触及这个水位,可认为内存严重不足,当前进程就会被堵住,kernel会直接在这个进程的进程上下文里面做直接页面回收。

这三个值的计算是通过min_free_kbytes来决定的,

1、先通过init_per_zone_wmark_min() 来计算min_free_kbytes的值:

lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SHIFT - 10);
//所有管理的页面减去高水位页面 managed - high_pages
min_free_kbytes = sqrt(lowmem_kbytes*16)

2、再通过setup_per_zone_wmarks()函数计算各项水位:
大体的计算公式如下:

pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);   //换算成page_number
pages_low = **extra_free_kbytes** >> (PAGE_SHIFT - 10);  
//PAGE_SHIFT 为12,相当于一个page的size,4K,2的12次方是4096
对每个非highmem的zone,lowmem_pages += zone->managed_pages
for_each_zone(){
      min = (u64)pages_min * zone->managed_pages;
      do_div(min, lowmem_pages);  //商保存在min,余数是返回值
      
      low =  (u64)pages_low * zone->managed_pages;
      do_div(min, vm_total_pages); 
  
      zone->**watermark_boost**= 0; //这个参数下一节讲
      zone->watermark[WMARK_MIN] = min;
      zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + low + min;
      zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + low + min*2;
}

网上资料看/proc/sys/vm/min_free_kbytes 范围在128KB~64M 这部分我没看到,看了下自己手机是40M左右,先不管了

min_free_kbytes大小调节有2个方向:

  • min_free_kbytes设置过大,相当于提高了了低水位,会提前唤醒kswapd(在实际项目中,尤其是ARM平台,会影响功耗)。另外预留太多,给普通进程的内存就少了。
  • min_free_kbytes设置过小,高优先级进程或者内核线程在紧急情况可能分配不到内存,容易进死锁。min_free_kbytes设的过小,则会导致系统预留内存过小。kswapd回收的过程中也会有少量的内存分配行为(会设上PF_MEMALLOC)标志,这个标志会允许kswapd使用预留内存;另外一种情况是被OOM选中杀死的进程在退出过程中,如果需要申请内存也可以使用预留部分。这两种情况下让他们使用预留内存可以避免系统进入deadlock状态。

extra_free_kbytes

眼尖的老哥可能在上一节中已经发现这个参数了

这个参数是从上层框架中传设置的,在这个文件中frameworks/base/services/core/java/com/android/server/am/ProcessList.java
通过计算屏幕分辨率 长宽公式得出 ,所以高端手机,尤其2K屏的机器这个值就特别大
需要注意,高低端机frameworks如果共基线,这里就会出现问题!

int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
int reserve_adj = Resources.getSystem().getInteger(
        com.android.internal.R.integer.config_extraFreeKbytesAdjust); 
/*通过名单控制,看了下我手机,默认为0*/
int reserve_abs = Resources.getSystem().getInteger(
        com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
/*通过名单控制,看了下我手机,默认为-1*/
if (reserve_abs >= 0) {
            reserve = reserve_abs;
        }
if (reserve_adj != 0) {
        reserve += reserve_adj;
        if (reserve < 0) {
                reserve = 0;
        }
 }

我们在日常项目中,这个值一般都会增大,2倍(中高端)到3倍(低端机)左右,卡顿和低内存问题会有明显改善!

lowmem_reserve_ratio

就是控制这个数组的protection: (0, 8675, 8677),单位是page。
数组大小就是zone的个数,这个在函数setup_per_zone_lowmem_reserve中计算

这个数组是为了防止过度从低端zone分配,比如DMA_ZONE;防止系统过早在低端zone中触发oom,而高端zone中内存充足。
ZONE_NORMAL中的protection都是0,无需预留。

这个参数是用来判断某次分配是否满足
详看zone_watermark_ok(): 判断的条件很多,不过这里用到的就这部分

if (free_pages <= min + z->lowmem_reserve[classzone_idx])
    return false;

classzone_idx是一通计算决定的首选zone,判断当前free页面是否大于min水位加上zone对应的预留页面

watermark_boost_factor

这个看名字很唬人,其实很简单。就是为了防止外碎片化,临时提高水位提前唤醒kswapd回收内存,这个是Linux 5.0后新增的,不过高通也都移植到了4.19平台。

默认是15000,相当于把HIGH水位提升到150%,设置为0就关闭了。

#define min_wmark_pages(z) (z->_watermark[WMARK_MIN] + z->watermark_boost/2)
#define low_wmark_pages(z) (z->_watermark[WMARK_LOW] + z->watermark_boost/2)
#define high_wmark_pages(z) (z->_watermark[WMARK_HIGH] + z->watermark_boost)
#define wmark_pages(z, i) (z->_watermark[i] + (((i) == WMARK_HIGH) ? (z->watermark_boost) : (z->watermark_boost / 2)))

看到没!好家伙给每个水位都加上去了!

在偷页函数中
/*补充偷页行为,如果order比较大,直接偷一个block(4MB)
 *否则的话,把块上的空闲页移动到需要的类型,再查看已经分配了多少页,如果兼容的或者free页超过一半,就修改这个block的类型
 */
static void steal_suitable_fallback()
{
      boost_watermark(zone);
}

static inline void boost_watermark(struct zone *zone)
{
    unsigned long max_boost;

    if (!watermark_boost_factor || !boost_eligible(zone))   //设置0就return掉
        return;
        /* high水位 x watermark_boost_factor / 10000 */
    max_boost = mult_frac(zone->_watermark[WMARK_HIGH],
            watermark_boost_factor, 10000);

    /*
     * high watermark may be uninitialised if fragmentation occurs
     * very early in boot so do not boost. We do not fall
     * through and boost by pageblock_nr_pages as failing
     * allocations that early means that reclaim is not going
     * to help and it may even be impossible to reclaim the
     * boosted watermark resulting in a hang.
     */
    if (!max_boost)
        return;
        /*pageblock_nr_pages 一半就是1024*/
    max_boost = max(pageblock_nr_pages, max_boost);

    zone->watermark_boost = min(zone->watermark_boost +     pageblock_nr_pages, max_boost);
}

有个问题就是,watermark_boost_factor节点在sysctl.c中的回调函数,只是个节点的读写,没有用,15000的默认值似乎只能通过修改代码去调整...

watermark_scale_factor

控制两个水位间的差距,取zone总内存的百分比和min/4取最大值,作为水位间的间隔:

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

看节点信息,这个数值在1-1000之间,所以两个水位之间的差值可以是总内存的0.1%到10%,看了下我手机上默认是1。

{
        .procname   = "watermark_scale_factor",
        .data       = &watermark_scale_factor,
        .maxlen     = sizeof(watermark_scale_factor),
        .mode       = 0644,
        .proc_handler   = watermark_scale_factor_sysctl_handler,
        .extra1     = &one,
        .extra2     = &one_thousand,
    },

而且这个参数的回调函数中,每次修改都会重新设定水位哟

int watermark_scale_factor_sysctl_handler(struct ctl_table *table, int write,
    void __user *buffer, size_t *length, loff_t *ppos)
{
    int rc;

    rc = proc_dointvec_minmax(table, write, buffer, length, ppos);
    if (rc)
        return rc;

    if (write)
        setup_per_zone_wmarks();

    return 0;
}

以上

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容