oom killer &lmkd killer

2023-05-16

目录

oom killer&reaper

task 进程内存回收

杀进程内存回收

lmkd killer

psi 

vmpressure

事件通知

内核psi 实现

内核vmpressure


oom killer&reaper

kernel-4.19/mm/page_alloc.c

void show_free_areas()

5558  static void show_migration_types(unsigned char type)
5559  {
5560      static const char types[MIGRATE_TYPES] = {
5561          [MIGRATE_UNMOVABLE]    = 'U',
5562          [MIGRATE_MOVABLE]    = 'M',
5563          [MIGRATE_RECLAIMABLE]    = 'E',
5564          [MIGRATE_HIGHATOMIC]    = 'H',
5565  #ifdef CONFIG_CMA
5566          [MIGRATE_CMA]        = 'C',
5567  #endif
5568  #ifdef CONFIG_MEMORY_ISOLATION
5569          [MIGRATE_ISOLATE]    = 'I',
5570  #endif
5571      };
5572      char tmp[MIGRATE_TYPES + 1];
5573      char *p = tmp;
5574      int i;
5575  
5576      for (i = 0; i < MIGRATE_TYPES; i++) {
5577          if (type & (1 << i))
5578              *p++ = types[i];
5579      }
5580  
5581      *p = '\0';
5582      printk(KERN_CONT "(%s) ", tmp);
5583  }
5584  

07-26 07:17:13.012294   665   665 I tombstoned: received crash request for pid 1666

07-26 07:17:13.069550  1666  1678 I system_server: Wrote stack traces to tombstoned
07-26 07:17:13.069883   665   665 E tombstoned: Traces for pid 1666 written to: trace_14

free 内存:[52244.829414] Normal free:176476kB min:8084kB low:41816kB high:57100kB 

低于low 水位

<4>[51754.901217] Normal free:37948kB min:8084kB low:41816kB high:57100kB active_anon:1186056kB inactive_anon:902712kB active_file:478228kB inactive_file:771480kB unevictable:160100kB writepending:180kB present:5242872kB managed:5095344kB mlocked:160100kB kernel_stack:87272kB pagetables:112028kB bounce:0kB free_pcp:7580kB local_pcp:1000kB free_cma:0kB
<4>[52244.829414] Normal free:176476kB min:8084kB low:41816kB high:57100kB active_anon:1017432kB inactive_anon:933272kB active_file:351592kB inactive_file:674244kB unevictable:160108kB writepending:4572kB present:5242872kB managed:5095344kB mlocked:160108kB kernel_stack:88052kB pagetables:115716kB bounce:0kB free_pcp:4680kB local_pcp:184kB free_cma:4944kB
<4>[52617.818993] Normal free:13104kB min:8084kB low:41816kB high:57100kB active_anon:1249956kB inactive_anon:815476kB active_file:347316kB inactive_file:683292kB unevictable:160108kB writepending:1596kB present:5242872kB managed:5095344kB mlocked:160108kB kernel_stack:91604kB pagetables:123852kB bounce:0kB free_pcp:9056kB local_pcp:1092kB free_cma:696kB
<4>[52716.114577] Normal free:254152kB min:8084kB low:41816kB high:57100kB active_anon:851372kB inactive_anon:776516kB active_file:327688kB inactive_file:558232kB unevictable:160108kB writepending:780kB present:5242872kB managed:5095344kB mlocked:160108kB kernel_stack:78948kB pagetables:146820kB bounce:0kB free_pcp:5552kB local_pcp:536kB free_cma:23092kB
<4>[52813.015225] Normal free:42824kB min:8084kB low:41816kB high:57100kB active_anon:1100268kB inactive_anon:670188kB active_file:592840kB inactive_file:912928kB unevictable:160124kB writepending:848kB present:5242872kB managed:5095344kB mlocked:160124kB kernel_stack:80996kB pagetables:106528kB bounce:0kB free_pcp:6908kB local_pcp:428kB free_cma:496kB

内存信息:

<3>[52892.632369]  (6)[10640:updateBufferCou][ION]warn: alloc pages order: 1 time: 35948307 ns
<4>[52892.632380]  (6)[10640:updateBufferCou]active_anon:388097 inactive_anon:192252 isolated_anon:0
<4>[52892.632380]  active_file:161630 inactive_file:412807 isolated_file:0
<4>[52892.632380]  unevictable:40309 dirty:8659 writeback:114 unstable:0
<4>[52892.632380]  slab_reclaimable:66918 slab_unreclaimable:84971
<4>[52892.632380]  mapped:303466 shmem:24030 pagetables:35513 bounce:0
<4>[52892.632380]  free:104913 free_pcp:3369 free_cma:489
<4>[52892.632387]  (6)[10640:updateBufferCou]Node 0 active_anon:1552388kB inactive_anon:769008kB active_file:646520kB inactive_file:1651228kB unevictable:161236kB isolated(anon):0kB isolated(file):0kB mapped:1213864kB dirty:34636kB writeback:456kB shmem:96120kB writeback_tmp:0kB unstable:0kB all_unreclaimable? no
<4>[52892.632392] DMA32 free:347432kB min:4284kB low:22160kB high:30260kB active_anon:448000kB inactive_anon:176596kB active_file:175432kB inactive_file:576756kB unevictable:1112kB writepending:416kB present:2770188kB managed:2719800kB mlocked:1112kB kernel_stack:31020kB pagetables:43660kB bounce:0kB free_pcp:10512kB local_pcp:1332kB free_cma:1264kB
<4>[52892.632395]  (6)[10640:updateBufferCou]lowmem_reserve[]: 0 4975 4975
<4>[52892.632400] Normal free:72220kB min:8084kB low:41816kB high:57100kB active_anon:1104904kB inactive_anon:592640kB active_file:471088kB inactive_file:1074472kB unevictable:160124kB writepending:34480kB present:5242872kB managed:5095344kB mlocked:160124kB kernel_stack:75024kB pagetables:98392kB bounce:0kB free_pcp:2936kB local_pcp:624kB free_cma:692kB
<4>[52892.632401]  (6)[10640:updateBufferCou]lowmem_reserve[]: 0 0 0
<4>[52892.632404] DMA32: 2449*4kB (UMECH) 2355*8kB (UMECH) 207*16kB (UECH) 1*32kB (UECH) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 11884*4kB (UMECH) 4727*8kB (UMECH) 546*16kB (UECH) 7*32kB (UECH) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 16521*4kB (UMECH) 4075*8kB (UMECH) 258*16kB (UECH) 6*32kB (UECH) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 10031*4kB (UMECH) 2727*8kB (UMECH) 1490*16kB (UECH) 1014*32kB (UECH) 0*64kB 1*128kB (H) 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 347652kB
<4>[52892.632423] Normal: 2*4kB (UMEH) 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 368*4kB (UMEH) 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 1887*4kB (UMEH) 2489*8kB (MEH) 2*16kB (MEH) 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB 3384*4kB (UMEH) 2502*8kB (MEH) 321*16kB (MEH) 132*32kB (MEH) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 71884kB
<4>[52892.632438]  (6)[10640:updateBufferCou]643408 total pagecache pages
<4>[52892.632442]  (6)[10640:updateBufferCou]5042 pages in swap cache
<4>[52892.632444]  (6)[10640:updateBufferCou]Swap cache stats: add 5650558, delete 5645530, find 2491845/4236319
<4>[52892.632446]  (6)[10640:updateBufferCou]Free swap  = 2850816kB
<4>[52892.632448]  (6)[10640:updateBufferCou]Total swap = 4194300kB
<3>[52892.639497]  (5)[10648:camerahalserver][ION] ion_mm_heap_allocate warn: size: 6291456 time: 29212000 ns --12

kernel-4.19/drivers/staging/android/mtk_ion/mtk/ion_mm_heap.c   
static int ion_mm_heap_allocate   

ION &free_cma

<4>[51754.901177]  free:23018 free_pcp:2800 free_cma:0
<4>[51754.901201] DMA32 free:54124kB min:4284kB low:22160kB high:30260kB active_anon:764840kB inactive_anon:337536kB active_file:210404kB inactive_file:450608kB unevictable:1088kB writepending:388kB present:2770188kB managed:2719800kB mlocked:1088kB kernel_stack:42520kB pagetables:63680kB bounce:0kB free_pcp:3616kB local_pcp:480kB free_cma:0kB
<4>[51754.901217] Normal free:37948kB min:8084kB low:41816kB high:57100kB active_anon:1186056kB inactive_anon:902712kB active_file:478228kB inactive_file:771480kB unevictable:160100kB writepending:180kB present:5242872kB managed:5095344kB mlocked:160100kB kernel_stack:87272kB pagetables:112028kB bounce:0kB free_pcp:7580kB local_pcp:1000kB free_cma:0kB
<4>[52244.829368]  free:117621 free_pcp:3984 free_cma:1423
<4>[52244.829398] DMA32 free:294008kB min:4284kB low:22160kB high:30260kB active_anon:462680kB inactive_anon:400480kB active_file:150328kB inactive_file:500308kB unevictable:1096kB writepending:528kB present:2770188kB managed:2719800kB mlocked:1096kB kernel_stack:41244kB pagetables:57396kB bounce:0kB free_pcp:11256kB local_pcp:1424kB free_cma:748kB
<4>[52244.829414] Normal free:176476kB min:8084kB low:41816kB high:57100kB active_anon:1017432kB inactive_anon:933272kB active_file:351592kB inactive_file:674244kB unevictable:160108kB writepending:4572kB present:5242872kB managed:5095344kB mlocked:160108kB kernel_stack:88052kB pagetables:115716kB bounce:0kB free_pcp:4680kB local_pcp:184kB free_cma:4944kB
<4>[52617.818954]  free:14475 free_pcp:4365 free_cma:174

<3>[52104.023224]  (6)[865:HwBinder:820_1][ION] ion_mm_heap_allocate warn: size: 4833280 time: 11988847 ns --10
<3>[52149.067876]  (1)[820:allocator@4.0-s][ION] ion_mm_heap_allocate warn: size: 4644864 time: 15130462 ns --10
<3>[52177.676606]  (4)[865:HwBinder:820_1][ION] ion_mm_heap_allocate warn: size: 4833280 time: 12451308 ns --10
<3>[52244.648844]  (1)[865:HwBinder:820_1][ION] ion_mm_heap_allocate warn: size: 4587520 time: 11143693 ns --10
<3>[52244.853367]  (2)[865:HwBinder:820_1][ION] ion_mm_heap_allocate warn: size: 4640768 time: 48444693 ns --10
<3>[52281.490891]  (2)[1150:HwBinder:820_3][ION] ion_mm_heap_allocate warn: size: 4644864 time: 20040308 ns --10
<3>[52285.704463]  (5)[1150:HwBinder:820_3][ION] ion_mm_heap_allocate warn: size: 4833280 time: 14012308 ns --10
<3>[52342.068943]  (0)[1150:HwBinder:820_3][ION] ion_mm_heap_allocate warn: size: 4657152 time: 10820385 ns --10

oom-killer  触发 oom_reaper 回收内存

<6>[52422.931280]  (6)[85:oom_reaper][wlan][17241]nicGetPendingCmdInfo:(TX INFO) Get command: 000000003d899667, nicCmdEventQueryStatistics.cfi_jt [wlan_drv_gen4m], cmd=0x82, seq=125
<6>[52422.936286]  (6)[85:oom_reaper]oom_reaper: reaped process 32659 (ocess.gservices), now anon-rss:0kB, file-rss:0kB, shmem-rss:476kB
<7>[52422.942561]  (6)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)
<7>[52422.970486]  (1)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)
<6>[52422.972047]  (6)[85:oom_reaper]oom_reaper: reaped process 19935 (.android.gms.ui), now anon-rss:0kB, file-rss:8716kB, shmem-rss:804kB
<6>[52422.980762]  (6)[85:oom_reaper]oom_reaper: reaped process 16233 (id.printspooler), now anon-rss:0kB, file-rss:0kB, shmem-rss:456kB
<7>[52422.996885]  (7)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)
<6>[52422.999074]  (6)[85:oom_reaper]oom_reaper: reaped process 7292 (ndroid.calendar), now anon-rss:0kB, file-rss:7712kB, shmem-rss:2336kB
<6>[52423.019935]  (2)[128:watchdogd][wdtk] kick watchdog
<6>[52423.023571]  (6)[85:oom_reaper]oom_reaper: reaped process 31759 (id.apps.tachyon), now anon-rss:0kB, file-rss:416kB, shmem-rss:748kB
<6>[52423.029840]  (6)[85:oom_reaper]oom_reaper: reaped process 7126 (eng:pushservice), now anon-rss:0kB, file-rss:0kB, shmem-rss:468kB
<7>[52423.030867]  (7)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)
<6>[52423.045601]  (6)[85:oom_reaper]oom_reaper: reaped process 30465 (wps.moffice_eng), now anon-rss:0kB, file-rss:2880kB, shmem-rss:904kB
<6>[52423.051797]  (6)[85:oom_reaper]oom_reaper: reaped process 6593 (ocessService0:0), now anon-rss:0kB, file-rss:0kB, shmem-rss:528kB
<6>[52423.058919]  (6)[85:oom_reaper]oom_reaper: reaped process 30827 (ice_eng:gcmpush), now anon-rss:0kB, file-rss:0kB, shmem-rss:648kB
<6>[52423.064856]  (6)[85:oom_reaper]oom_reaper: reaped process 7146 (erseabackground), now anon-rss:0kB, file-rss:0kB, shmem-rss:476kB
<7>[52423.066918]  (7)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)
<6>[52423.070778]  (6)[85:oom_reaper]oom_reaper: reaped process 6658 (:widgetProvider), now anon-rss:0kB, file-rss:0kB, shmem-rss:480kB
<6>[52423.079906]  (6)[85:oom_reaper]oom_reaper: reaped process 6689 (office_eng:scan), now anon-rss:0kB, file-rss:0kB, shmem-rss:468kB
<3>[52423.085944]  (6)[26470:kworker/6:2][sensor_devinfo] sync_utc2scp_work 583 : kernel_ts: 1658790184.875517, 1658790184.875517
<3>[52423.086691] -(1)[27755:binder:20473_8][mtk_nanohub]IPI_SENSOR cannot find cmd!
<7>[52423.097788]  (1)[5106:mali-cmar-backe]mtk_dbgtop_dfd_timeout: before MTK_DBGTOP_LATCH_CTL2(0x603e8)

Low on memory

07-26 07:18:57.380075 16722 17348 I ActivityManager: Low on memory:

07-26 07:18:57.380399 16722 17348 I ActivityManager:   MemInfo:   677,896K slab,     9,172K shmem,   132,720K vm alloc,    39,216K page tables    37,468K kernel stack
07-26 07:18:57.380399 16722 17348 I ActivityManager:                5,924K buffers, 4,145,992K cached,   680,532K mapped, 1,117,072K free
07-26 07:18:57.380399 16722 17348 I ActivityManager:   ZRAM:   223,464K RAM, 4,194,300K swap total, 3,831,784K swap free
07-26 07:18:57.380399 16722 17348 I ActivityManager:   Free RAM: 5,513,732K
07-26 07:18:57.380399 16722 17348 I ActivityManager:        ION:   512,372K
07-26 07:18:57.380399 16722 17348 I ActivityManager:        GPU:         0K
07-26 07:18:57.380399 16722 17348 I ActivityManager:   Used RAM: 2,068,918K
07-26 07:18:57.380399 16722 17348 I ActivityManager:   Lost RAM:   381,511K


1314      @GuardedBy("mService")
1315      final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
1316          // If there are no longer any background processes running,
1317          // and the app that died was not running instrumentation,
1318          // then tell everyone we are now low on memory.
1319          if (!mService.mProcessList.haveBackgroundProcessLOSP()) {
1320              boolean doReport = Build.IS_DEBUGGABLE;
1321              final long now = SystemClock.uptimeMillis();
1322              if (doReport) {
1323                  if (now < (mLastMemUsageReportTime + 5 * 60 * 1000)) {
1324                      doReport = false;
1325                  } else {
1326                      mLastMemUsageReportTime = now;
1327                  }
1328              }

<4>[53359.909526] -(4)[3012:binder:1666_B]Some other process 3012:binder:1666_B want to send sig:9 to pid:16507 tgid:792 comm:FinalizerWatchd
<4>[53359.909996]  (4)[792:main]critical svc 792:main exit with 9 !

native 进程内存泄漏

从 event log 中可以看到进程的内存使用信息。

一般通过 am_pss 中能看个各个进程内存使用情况,某个 native 进程的 am_pss 中 uss 过高,大概率是它引起内存泄漏。

am_pss: Pid, UID, ProcessName, Pss, Uss。

am_meminfo: Cached,Free,Zram,Kernel,Native

am_low_memory: NumProcesses

vss (virtual set size) 虚拟内存,从进程地址空间统计,包括未实际申请到物理内存的部分。

rss (resident set size) 实际物理内存+共享库。共享库如果映射多个进程,不均摊。

pss ( proportional set size) 物理内存+均摊的共享内存。

uss (unique set size) 独占物理内存,不包含共享库部分。

虚拟地址内存泄漏

vss oom,多发生在 32 位应用。虚拟地址空间不足,无法申请到 vma,所以申请内存失败。

一般只有发生泄漏的应用会崩溃,物理内存情况可能使用并不多,虚拟内存可能接近 4G(32位)。

一般需要 smaps/maps 信息做进一步分析,确认哪种类型的 vma 占比较多,那么大概率是它泄漏(比如 libc, egl,ion,等等)。

ion 内存泄漏

ion 是 android 中特有的内存分配器,一般在 camera,图像中使用较多。

通过 ion 的 ioctl 接口,可以申请不同类型的内存,可以为指定进程预留,比如 物理地址空间连续(cma),为相机预留。

ion 申请的内存需要主动释放,不释放会存在泄漏。每一个 heap 中申请的都对应一个 ion_buffer,我们可以统计 ion buffer 中某个进程占比多少(高通支持)。

如果 ion 的总量特别大(比如 4/8G),那么大概率是 ion 泄漏。再通过每个进程的信息,确定到是哪个进程申请导致的泄漏。

slab 内存泄漏

slab 机制是内核申请小块内存的管理机制,同样需要主动 free。

task 进程内存回收

在内核执行分配内存不够时,会执行out_of_memory ,会wake_oom_reaper

创建oom 内核线程

679  static int __init oom_init(void)
680  {
681      oom_reaper_th = kthread_run(oom_reaper, NULL, "oom_reaper");
682      return 0;
683  }

唤醒oom 线程执行 oom_reaper

663  static void wake_oom_reaper(struct task_struct *tsk)
664  {
665      /* mm is already queued? */
666      if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
667          return;
668  
669      get_task_struct(tsk);
670  
671      spin_lock(&oom_reaper_lock);
672      tsk->oom_reaper_list = oom_reaper_list;   //保存之前的记录
673      oom_reaper_list = tsk;   //记录要回收内存的task 
674      spin_unlock(&oom_reaper_lock);
675      trace_wake_reaper(tsk->pid);
676      wake_up(&oom_reaper_wait);
677  }

对task 内存进行回收

643  static int oom_reaper(void *unused)
644  {
645      while (true) {
646          struct task_struct *tsk = NULL;
647  
648          wait_event_freezable(oom_reaper_wait, oom_reaper_list != NULL);
649          spin_lock(&oom_reaper_lock);
650          if (oom_reaper_list != NULL) {
651              tsk = oom_reaper_list;  //要回收内存的task 
652              oom_reaper_list = tsk->oom_reaper_list;   //还原之前的记录
653          }
654          spin_unlock(&oom_reaper_lock);
655  
656          if (tsk)
657              oom_reap_task(tsk);
658      }
659  
660      return 0;
661  }

对匿名和非VM_SHARED 页面回收
612  #define MAX_OOM_REAP_RETRIES 10
613  static void oom_reap_task(struct task_struct *tsk)
614  {
615      int attempts = 0;
616      struct mm_struct *mm = tsk->signal->oom_mm;
617  
618      /* Retry the down_read_trylock(mmap_sem) a few times */
619      while (attempts++ < MAX_OOM_REAP_RETRIES && !oom_reap_task_mm(tsk, mm))
620          schedule_timeout_idle(HZ/10);
621  
622      if (attempts <= MAX_OOM_REAP_RETRIES ||
623          test_bit(MMF_OOM_SKIP, &mm->flags))
624          goto done;
625  
626      pr_info("oom_reaper: unable to reap pid:%d (%s)\n",
627          task_pid_nr(tsk), tsk->comm);
628      debug_show_all_locks();
629  
630  done:
631      tsk->oom_reaper_list = NULL;
632  
633      /*
634       * Hide this mm from OOM killer because it has been either reaped or
635       * somebody can't call up_write(mmap_sem).
636       */
637      set_bit(MMF_OOM_SKIP, &mm->flags);
638  
639      /* Drop a reference taken by wake_oom_reaper */
640      put_task_struct(tsk);
641  }

杀进程内存回收

/**
1087   * out_of_memory - kill the "best" process when we run out of memory
1088   * @oc: pointer to struct oom_control
1089   *
1090   * If we run out of memory, we have the choice between either
1091   * killing a random task (bad), letting the system crash (worse)
1092   * OR try to be smart about which process to kill. Note that we
1093   * don't have to be perfect here, we just have to be good.
1094   */
1095  bool out_of_memory(struct oom_control *oc)
1096  {
1097      unsigned long freed = 0;
1098      enum oom_constraint constraint = CONSTRAINT_NONE;
1099  
1100      if (oom_killer_disabled)
1101          return false;
1102  
1103      if (!is_memcg_oom(oc)) {
1104          blocking_notifier_call_chain(&oom_notify_list, 0, &freed);   
1105          if (freed > 0)
1106              /* Got some memory back in the last second. */
1107              return true;
1108      }
1109  
1110      /*
1111       * If current has a pending SIGKILL or is exiting, then automatically
1112       * select it.  The goal is to allow it to allocate so that it may
1113       * quickly exit and free its memory.
1114       */
1115      if (task_will_free_mem(current)) {
1116          mark_oom_victim(current);
1117          wake_oom_reaper(current);
1118          return true;
1119      }
1120  
1121      /*
1122       * The OOM killer does not compensate for IO-less reclaim.
1123       * pagefault_out_of_memory lost its gfp context so we have to
1124       * make sure exclude 0 mask - all other users should have at least
1125       * ___GFP_DIRECT_RECLAIM to get here. But mem_cgroup_oom() has to
1126       * invoke the OOM killer even if it is a GFP_NOFS allocation.
1127       */
1128      if (oc->gfp_mask && !(oc->gfp_mask & __GFP_FS) && !is_memcg_oom(oc))
1129          return true;
1130  
1131      /*
1132       * Check if there were limitations on the allocation (only relevant for
1133       * NUMA and memcg) that may require different handling.
1134       */
1135      constraint = constrained_alloc(oc);
1136      if (constraint != CONSTRAINT_MEMORY_POLICY)
1137          oc->nodemask = NULL;
1138      check_panic_on_oom(oc, constraint);
1139  
1140      if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&
1141          current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&
1142          current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
1143          get_task_struct(current);
1144          oc->chosen = current;
1145          oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)");
1146          return true;
1147      }
1148  
1149      select_bad_process(oc);
1150      /* Found nothing?!?! */
1151      if (!oc->chosen) {
1152          dump_header(oc, NULL);
1153          pr_warn("Out of memory and no killable processes...\n");
1154          /*
1155           * If we got here due to an actual allocation at the
1156           * system level, we cannot survive this and will enter
1157           * an endless loop in the allocator. Bail out now.
1158           */
1159          if (!is_sysrq_oom(oc) && !is_memcg_oom(oc)) {
1160  #ifdef CONFIG_PAGE_OWNER
1161              print_max_page_owner();
1162  #endif
1163              panic("System is deadlocked on memory\n");
1164          }
1165      }
1166      if (oc->chosen && oc->chosen != (void *)-1UL)
1167          oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :
1168                   "Memory cgroup out of memory");
1169      return !!oc->chosen;
1170  }

  1. 首先通知 oom_notify_list 链表的订阅者:依据通知链(notification chains)机制,通知注册了 oom_notify_list 的模块释放内存;如果订阅者能够处理 OOM,释放了内存则会退出 OOM killer,不执行后续操作。

  2. 如果当前 task 存在 pending 的 SIGKILL,或者已经退出的时,会释放当前进程的资源。包括和 task 共享同一个内存描述符 mm_struct 的进程、线程也会被杀掉。

  3. 对于 IO-less 的回收,依据 gfp_mask 判断,如果 1) 分配的是非 FS 操作类型的分配,并且 2)不是 cgroup 的内存 OOM -> 直接退出 oom-killer。

  4. 检查内存分配的约束(例如 NUMA),有 CONSTRAINT_NONE, CONSTRAINT_CPUSET,CONSTRAINT_MEMORY_POLICY, CONSTRAINT_MEMCG 类型。

  5. 检查 /proc/sys/vm/panic_on_oom 的设置,做操作;为0 就不会直接panic;为1 可能 panic,也可能尝试 oom_killer。如果 panic_on_oom 设置的为 2,则进程直接 panic 强制退出
  6. /proc/sys/vm/oom_kill_allocating_task 为 true 的时候,调用 oom_kill_process 直接 kill 掉当前想要分配内存的进程 (此进程能够被 kill 时)。

  7. select_bad_process(),选择最合适的进程,调用 oom_kill_process。

  8. 如果没有合适的进程,如果非 sysrq 和 memcg,则 panic 强制退出。

通过oom_kill_process 可能会调用wake_oom_reaper 回收内存。

Android OOM、OOMKillery以及LMK相关概念_尹杰Enjoy your code的博客-CSDN博客

Kubernetes 单机侧的驱逐策略总结_米开朗基杨的博客-CSDN博客

lmkd killer

lmk & shrinker

这个不同的android 版本,实现有些差异

Linux内核机制总结内存管理之页回收(二十三)_张衡天的博客-CSDN博客_linux内存回收命令

lowmemorykiller驱动 - ruby.dongyu - 博客园

binder.c

__init binder_init(void){

    binder_alloc_shrinker_init();

    init_binder_device(device_name);

    init_binderfs();

}

一个例子就只binder 会注册shrinker ,低内存会通过shrinke_slab 进行回收bindr_alloc 分配的页面。


ProcessList.java
    // Low Memory Killer Daemon command codes.
346      // These must be kept in sync with lmk_cmd definitions in lmkd.h
347      //
348      // LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
349      // LMK_PROCPRIO <pid> <uid> <prio>
350      // LMK_PROCREMOVE <pid>
351      // LMK_PROCPURGE
352      // LMK_GETKILLCNT
353      // LMK_SUBSCRIBE
354      // LMK_PROCKILL
355      // LMK_UPDATE_PROPS
356      // LMK_KILL_OCCURRED
357      // LMK_STATE_CHANGED
358      static final byte LMK_TARGET = 0;
359      static final byte LMK_PROCPRIO = 1;
360      static final byte LMK_PROCREMOVE = 2;
361      static final byte LMK_PROCPURGE = 3;
362      static final byte LMK_GETKILLCNT = 4;
363      static final byte LMK_SUBSCRIBE = 5;
364      static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
365      static final byte LMK_UPDATE_PROPS = 7;
366      static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
367      static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed


 public static void setOomAdj(int pid, int uid, int amt) {
       long start = SystemClock.elapsedRealtime();
1508          ByteBuffer buf = ByteBuffer.allocate(4 * 4);
1509          buf.putInt(LMK_PROCPRIO);
1510          buf.putInt(pid);
1511          buf.putInt(uid);
1512          buf.putInt(amt);
1513          writeLmkd(buf, null);
1514          long now = SystemClock.elapsedRealtime();
1515          if ((now-start) > 250) {
1516              Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
1517                      + " = " + amt);
1518          }
1519      }
1520  
 

ams 根据进程状态,更新oom ,这样就会跟lmkd 通信

system/memory/lmkd/lmkd.cpp


7  /*
118   * PSI monitor tracking window size.
119   * PSI monitor generates events at most once per window,
120   * therefore we poll memory state for the duration of
121   * PSI_WINDOW_SIZE_MS after the event happens.
122   */
123  #define PSI_WINDOW_SIZE_MS 1000
124  /* Polling period after PSI signal when pressure is high */
125  #define PSI_POLL_PERIOD_SHORT_MS 10
126  /* Polling period after PSI signal when pressure is low */
127  #define PSI_POLL_PERIOD_LONG_MS 100
128  
129  #define min(a, b) (((a) < (b)) ? (a) : (b))
130  #define max(a, b) (((a) > (b)) ? (a) : (b))
131  
132  #define FAIL_REPORT_RLIMIT_MS 1000
133  
134  /*
135   * System property defaults
136   */
137  /* ro.lmk.swap_free_low_percentage property defaults */
138  #define DEF_LOW_SWAP 10
139  /* ro.lmk.thrashing_limit property defaults */
140  #define DEF_THRASHING_LOWRAM 30
141  #define DEF_THRASHING 100
142  /* ro.lmk.thrashing_limit_decay property defaults */
143  #define DEF_THRASHING_DECAY_LOWRAM 50
144  #define DEF_THRASHING_DECAY 10
145  /* ro.lmk.psi_partial_stall_ms property defaults */
146  #define DEF_PARTIAL_STALL_LOWRAM 200
147  #define DEF_PARTIAL_STALL 70
148  /* ro.lmk.psi_complete_stall_ms property defaults */
149  #define DEF_COMPLETE_STALL 700
150  
151  #define LMKD_REINIT_PROP "lmkd.reinit"
152  
153  /* default to old in-kernel interface if no memory pressure events */
154  static bool use_inkernel_interface = true;
155  static bool has_inkernel_module;
156  
157  /* memory pressure levels */
158  enum vmpressure_level {
159      VMPRESS_LEVEL_LOW = 0,
160      VMPRESS_LEVEL_MEDIUM,
161      VMPRESS_LEVEL_CRITICAL,
162      VMPRESS_LEVEL_SUPER_CRITICAL,
163      VMPRESS_LEVEL_COUNT
164  };


int main(int argc, char **argv) {

3949      if (!init()) {
3950          if (!use_inkernel_interface) {
3951              /*
3952               * MCL_ONFAULT pins pages as they fault instead of loading
3953               * everything immediately all at once. (Which would be bad,
3954               * because as of this writing, we have a lot of mapped pages we
3955               * never use.) Old kernels will see MCL_ONFAULT and fail with
3956               * EINVAL; we ignore this failure.
3957               *
3958               * N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
3959               * pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
3960               * in pages.
3961               */
3962              /* CAP_IPC_LOCK required */
3963              if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
3964                  ALOGW("mlockall failed %s", strerror(errno));
3965              }
3966  
3967              /* CAP_NICE required */
3968              struct sched_param param = {
3969                      .sched_priority = 1,
3970              };
3971              if (sched_setscheduler(0, SCHED_FIFO, &param)) {
3972                  ALOGW("set SCHED_FIFO failed %s", strerror(errno));
3973              }
3974          }
3975  
3976          mainloop();
3977      }
3978  
3979      android_log_destroy(&ctx);
3980  
3981      ALOGI("exiting");
3982      return 0;
3983  }

static int init(void) {
3547      static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };

3552      struct reread_data file_data = {
3553          .filename = ZONEINFO_PATH,
3554          .fd = -1,
3555      };
3556      struct epoll_event epev;
3557      int pidfd;
3558      int i;
3559      int ret;
3560  
3561      page_k = sysconf(_SC_PAGESIZE);
3562      if (page_k == -1)
3563          page_k = PAGE_SIZE;
3564      page_k /= 1024;
3565  
3566      epollfd = epoll_create(MAX_EPOLL_EVENTS);
3567      if (epollfd == -1) {
3568          ALOGE("epoll_create failed (errno=%d)", errno);
3569          return -1;
3570      }
3571  
3572      // mark data connections as not connected
3573      for (int i = 0; i < MAX_DATA_CONN; i++) {
3574          data_sock[i].sock = -1;
3575      }
3576  
3577      ctrl_sock.sock = android_get_control_socket("lmkd");
3578      if (ctrl_sock.sock < 0) {
3579          ALOGE("get lmkd control socket failed");
3580          return -1;
3581      }
3582  
3583      ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
3584      if (ret < 0) {
3585          ALOGE("lmkd control socket listen failed (errno=%d)", errno);
3586          return -1;
3587      }
3588  
3589      epev.events = EPOLLIN;
3590      ctrl_sock.handler_info.handler = ctrl_connect_handler;
3591      epev.data.ptr = (void *)&(ctrl_sock.handler_info);
3592      if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
3593          ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
3594          return -1;
3595      }
3596      maxevents++;

if (!init_monitors()) {
3618              return -1;
3619          }
3620          /* let the others know it does support reporting kills */
3621          property_set("sys.lmk.reportkills", "1");
3622      }

..........

}
 

  /* default to old in-kernel interface if no memory pressure events */
 static bool use_inkernel_interface = true;
 static bool has_inkernel_module;


  #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
  #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"

默认使用以前的mlk 机制,也就是内核lmk 模块低内存杀应用,对应的水位和adj 对应INKERNEL_MINFREE_PATH 和 INKERNEL_ADJ_PATH ,新的lmk 机制是通过内存压力事件psi 来杀应用,这样不存在内核模块对应的adj 和 内存水位文件,这样就会has_inkernel_module 和 use_inkernel_interface  都为false ,使用用户空间lmk ,用户空间lmk内存压力事件上报方式有vmpressure和psi两种方式

psi 

3359  static bool init_monitors() {
3360      /* Try to use psi monitor first if kernel has it */
3361      use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
3362          init_psi_monitors();
3363      /* Fall back to vmpressure */
3364      if (!use_psi_monitors &&
3365          (!init_mp_common(VMPRESS_LEVEL_LOW) ||
3366          !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
3367          !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
3368          ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
3369          return false;
3370      }
3371      if (use_psi_monitors) {
3372          ALOGI("Using psi monitors for memory pressure detection");
3373      } else {
3374          ALOGI("Using vmpressure for memory pressure detection");
3375      }
3376      return true;
3377  }
3378  


3239  static bool init_psi_monitors() {
3240      /*
3241       * When PSI is used on low-ram devices or on high-end devices without memfree levels
3242       * use new kill strategy based on zone watermarks, free swap and thrashing stats
3243       */
3244      bool use_new_strategy =
3245          property_get_bool("ro.lmk.use_new_strategy", low_ram_device || !use_minfree_levels);
3246  
3247      /* In default PSI mode override stall amounts using system properties */
3248      if (use_new_strategy) {
3249          /* Do not use low pressure level */
3250          psi_thresholds[VMPRESS_LEVEL_LOW].threshold_ms = 0;
3251          psi_thresholds[VMPRESS_LEVEL_MEDIUM].threshold_ms = psi_partial_stall_ms;
3252          psi_thresholds[VMPRESS_LEVEL_CRITICAL].threshold_ms = psi_complete_stall_ms;
3253      }
3254  
3255      if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
3256          return false;
3257      }
3258      if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
3259          destroy_mp_psi(VMPRESS_LEVEL_LOW);
3260          return false;
3261      }
3262      if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
3263          destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
3264          destroy_mp_psi(VMPRESS_LEVEL_LOW);
3265          return false;
3266      }
3267      return true;
3268  }

208  static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
209      { PSI_SOME, 70 },    /* 70ms out of 1sec for partial stall */
210      { PSI_SOME, 100 },   /* 100ms out of 1sec for partial stall */
211      { PSI_FULL, 70 },    /* 70ms out of 1sec for complete stall */
212  };

其中partial stall指的是该时间段内有一个或多个task因为缺少资源而等待,

complete stall指的是该时间段内所有的task都因得不到资源而等待。

psi可以是针对system-wide的,也可以是per-cgroup的。


 

3195  static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
3196      int fd;
3197  
3198      /* Do not register a handler if threshold_ms is not set */
3199      if (!psi_thresholds[level].threshold_ms) {
3200          return true;
3201      }
3202  

####设置psi 参数 到  /proc/pressure/memory
3203      fd = init_psi_monitor(psi_thresholds[level].stall_type,
3204          psi_thresholds[level].threshold_ms * US_PER_MS,
3205          PSI_WINDOW_SIZE_MS * US_PER_MS);
3206  
3207      if (fd < 0) {
3208          return false;
3209      }
3210  

######监听内核发送满足参数的事件 及 执行函数
3211      vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
3212      vmpressure_hinfo[level].data = level;
3213      if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
3214          destroy_psi_monitor(fd);
3215          return false;
3216      }
3217      maxevents++;
3218      mpevfd[level] = fd;
3219  
3220      return true;
3221  }

将psi 参数写入 /proc/pressure/memory ,并监听这个文件句柄对应的事件

#define PSI_MON_FILE_MEMORY "/proc/pressure/memory"
int init_psi_monitor(enum psi_stall_type stall_type,
39               int threshold_us, int window_us) {
40      int fd;
41      int res;
42      char buf[256];
43  
44      fd = TEMP_FAILURE_RETRY(open(PSI_MON_FILE_MEMORY, O_WRONLY | O_CLOEXEC));
45      if (fd < 0) {
46          ALOGE("No kernel psi monitor support (errno=%d)", errno);
47          return -1;
48      }
49  
50      switch (stall_type) {
51      case (PSI_SOME):
52      case (PSI_FULL):
53          res = snprintf(buf, sizeof(buf), "%s %d %d",
54              stall_type_name[stall_type], threshold_us, window_us);
55          break;
56      default:
57          ALOGE("Invalid psi stall type: %d", stall_type);
58          errno = EINVAL;
59          goto err;
60      }
61  
62      if (res >= (ssize_t)sizeof(buf)) {
63          ALOGE("%s line overflow for psi stall type '%s'",
64              PSI_MON_FILE_MEMORY, stall_type_name[stall_type]);
65          errno = EINVAL;
66          goto err;
67      }
68  
69      res = TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf) + 1));
70      if (res < 0) {
71          ALOGE("%s write failed for psi stall type '%s'; errno=%d",
72              PSI_MON_FILE_MEMORY, stall_type_name[stall_type], errno);
73          goto err;
74      }
75  
76      return fd;
77  
78  err:
79      close(fd);
80      return -1;
81  }

参考

https://www.jianshu.com/p/e01063abe31cPSI - Pressure Stall Information — The Linux Kernel documentationhttps://www.jianshu.com/p/e01063abe31c

vmpressure

3359  static bool init_monitors() {
3360      /* Try to use psi monitor first if kernel has it */
3361      use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
3362          init_psi_monitors();
3363      /* Fall back to vmpressure */
3364      if (!use_psi_monitors &&
3365          (!init_mp_common(VMPRESS_LEVEL_LOW) ||
3366          !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
3367          !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
3368          ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
3369          return false;
3370      }

.......

}




3270  static bool init_mp_common(enum vmpressure_level level) {
3271      int mpfd;
3272      int evfd;
3273      int evctlfd;
3274      char buf[256];
3275      struct epoll_event epev;
3276      int ret;
3277      int level_idx = (int)level;
3278      const char *levelstr = level_name[level_idx];
3279  
3280      /* gid containing AID_SYSTEM required */
3281      mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
3282      if (mpfd < 0) {
3283          ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
3284          goto err_open_mpfd;
3285      }
3286  
3287      evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
3288      if (evctlfd < 0) {
3289          ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
3290          goto err_open_evctlfd;
3291      }
3292  
3293      evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
3294      if (evfd < 0) {
3295          ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
3296          goto err_eventfd;
3297      }
3298  
3299      ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
3300      if (ret >= (ssize_t)sizeof(buf)) {
3301          ALOGE("cgroup.event_control line overflow for level %s", levelstr);
3302          goto err;
3303      }
3304  
3305      ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
3306      if (ret == -1) {
3307          ALOGE("cgroup.event_control write failed for level %s; errno=%d",
3308                levelstr, errno);
3309          goto err;
3310      }
3311  
3312      epev.events = EPOLLIN;
3313      /* use data to store event level */
3314      vmpressure_hinfo[level_idx].data = level_idx;
3315      vmpressure_hinfo[level_idx].handler = mp_event_common;
3316      epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
3317      ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
3318      if (ret == -1) {
3319          ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
3320          goto err;
3321      }
3322      maxevents++;
3323      mpevfd[level] = evfd;
3324      close(evctlfd);
3325      return true;
3326  

}

/dev/memcg/memory.pressure_level

/dev/memcg/cgroup.event_control

向/dev/memcg/cgroup.event_control节点写入"evfd mpfd levelstr"即可注册监听,

其中evfd告诉内核事件发生后通知谁,mpfd表示监听的是什么事件(这里为memory.pressure_level),levelstr表示监听内存压力级别,可以是low,medium,critical

监听到事件调用  mp_event_common

static void mp_event_common(int data, uint32_t events, struct polling_params *poll_params) {
2960      unsigned long long evcount;
2961      int64_t mem_usage, memsw_usage;
2962      int64_t mem_pressure;
2963      union meminfo mi;
2964      struct zoneinfo zi;
2965      struct timespec curr_tm;
2966      static unsigned long kill_skip_count = 0;
2967      enum vmpressure_level level = (enum vmpressure_level)data;
2968      long other_free = 0, other_file = 0;
2969      int min_score_adj;
2970      int minfree = 0;
2971      static struct reread_data mem_usage_file_data = {
2972          .filename = MEMCG_MEMORY_USAGE,
2973          .fd = -1,
2974      };
2975      static struct reread_data memsw_usage_file_data = {
2976          .filename = MEMCG_MEMORYSW_USAGE,
2977          .fd = -1,

####ro.lmk.use_minfree_levels    这里确定是否用之前的 lmkd  水位和对应的 adj 杀应用
 if (use_minfree_levels) {
3053          int i;
3054  
3055          other_free = mi.field.nr_free_pages - zi.totalreserve_pages;
3056          if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) {
3057              other_file = (mi.field.nr_file_pages - mi.field.shmem -
3058                            mi.field.unevictable - mi.field.swap_cached);
3059          } else {
3060              other_file = 0;
3061          }
3062  
3063          min_score_adj = OOM_SCORE_ADJ_MAX + 1;
3064          for (i = 0; i < lowmem_targets_size; i++) {
3065              minfree = lowmem_minfree[i];
3066              if (other_free < minfree && other_file < minfree) {
3067                  min_score_adj = lowmem_adj[i];
3068                  break;
3069              }
3070          }
3071  
3072          if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
3073              if (debug_process_killing) {
3074                  ALOGI("Ignore %s memory pressure event "
3075                        "(free memory=%ldkB, cache=%ldkB, limit=%ldkB)",
3076                        level_name[level], other_free * page_k, other_file * page_k,
3077                        (long)lowmem_minfree[lowmem_targets_size - 1] * page_k);
3078              }
3079              return;
3080          }
3081  
3082          goto do_kill;
3083      }


    if (level == VMPRESS_LEVEL_LOW) {
3086          record_low_pressure_levels(&mi);
3087      }
3088  
3089      if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
3090          /* Do not monitor this pressure level */
3091          return;
3092      }
3093  
3094      if ((mem_usage = get_memory_usage(&mem_usage_file_data)) < 0) {
3095          goto do_kill;
3096      }
3097      if ((memsw_usage = get_memory_usage(&memsw_usage_file_data)) < 0) {
3098          goto do_kill;
3099      }
3100  
3101      // Calculate percent for swappinness.
3102      mem_pressure = (mem_usage * 100) / memsw_usage;
3103  
3104      if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
3105          // We are swapping too much.
3106          if (mem_pressure < upgrade_pressure) {
3107              level = upgrade_level(level);
3108              if (debug_process_killing) {
3109                  ALOGI("Event upgraded to %s", level_name[level]);
3110              }
3111          }
3112      }
3113  
3114      // If we still have enough swap space available, check if we want to
3115      // ignore/downgrade pressure events.
3116      if (mi.field.free_swap >=
3117          mi.field.total_swap * swap_free_low_percentage / 100) {
3118          // If the pressure is larger than downgrade_pressure lmk will not
3119          // kill any process, since enough memory is available.
3120          if (mem_pressure > downgrade_pressure) {
3121              if (debug_process_killing) {
3122                  ALOGI("Ignore %s memory pressure", level_name[level]);
3123              }
3124              return;
3125          } else if (level == VMPRESS_LEVEL_CRITICAL && mem_pressure > upgrade_pressure) {
3126              if (debug_process_killing) {
3127                  ALOGI("Downgrade critical memory pressure");
3128              }
3129              // Downgrade event, since enough memory available.
3130              level = downgrade_level(level);
3131          }
3132      }
3133  
3134  do_kill:
3135      if (low_ram_device) {
3136          /* For Go devices kill only one task */
3137          if (find_and_kill_process(level_oomadj[level], NULL, &mi, &wi, &curr_tm) == 0) {
3138              if (debug_process_killing) {
3139                  ALOGI("Nothing to kill");
3140              }
3141          }
3142      } else {
3143          int pages_freed;
3144          static struct timespec last_report_tm;
3145          static unsigned long report_skip_count = 0;
3146  
3147          if (!use_minfree_levels) {
3148              /* Free up enough memory to downgrate the memory pressure to low level */
3149              if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
3150                  if (debug_process_killing) {
3151                      ALOGI("Ignoring pressure since more memory is "
3152                          "available (%" PRId64 ") than watermark (%" PRId64 ")",
3153                          mi.field.nr_free_pages, low_pressure_mem.max_nr_free_pages);
3154                  }
3155                  return;
3156              }
3157              min_score_adj = level_oomadj[level];
3158          }
3159  
3160          pages_freed = find_and_kill_process(min_score_adj, NULL, &mi, &wi, &curr_tm);
3161  
3162          if (pages_freed == 0) {
3163              /* Rate limit kill reports when nothing was reclaimed */
3164              if (get_time_diff_ms(&last_report_tm, &curr_tm) < FAIL_REPORT_RLIMIT_MS) {
3165                  report_skip_count++;
3166                  return;
3167              }
3168          }
3169  
3170          /* Log whenever we kill or when report rate limit allows */
3171          if (use_minfree_levels) {
3172              ALOGI("Reclaimed %ldkB, cache(%ldkB) and free(%" PRId64 "kB)-reserved(%" PRId64 "kB) "
3173                  "below min(%ldkB) for oom_score_adj %d",
3174                  pages_freed * page_k,
3175                  other_file * page_k, mi.field.nr_free_pages * page_k,
3176                  zi.totalreserve_pages * page_k,
3177                  minfree * page_k, min_score_adj);
3178          } else {
3179              ALOGI("Reclaimed %ldkB at oom_score_adj %d", pages_freed * page_k, min_score_adj);
3180          }
3181  
3182          if (report_skip_count > 0) {
3183              ALOGI("Suppressed %lu failed kill reports", report_skip_count);
3184              report_skip_count = 0;
3185          }
3186  
3187          last_report_tm = curr_tm;
3188      }
3189      if (is_waiting_for_kill()) {
3190          /* pause polling if we are waiting for process death notification */
3191          poll_params->update = POLLING_PAUSE;
3192      }
3193  }

事件通知

PSI

使用psi_memstall_enter和psi_memstall_leave包裹相关内存操作,实现事件信息统计。这些操作有以下几种:

kernel/msm-4.14/mm/vmscan.c

在try_to_free_mem_cgroup_pages函数中,调用do_try_to_free_pages进行内存回收的时候

kswapd在调用balance_pgdat进行内存回收的时候

kernel/msm-4.14/mm/compaction.c

kcompactd函数在进行内存压缩的时候

kernel/msm-4.14/mm/page_alloc.c

调用__alloc_pages_direct_compact进行内存压缩的时候

调用__perform_reclaim进行内存回收的时候

kernel/msm-4.14/mm/filemap.c

调用wait_on_page_locked,等待文件页就绪的时候

//满足注册条件后开始上报

vmpressure

通过以下几条路径触发事件的上报:

kernel/msm-4.14/mm/vmscan.c:

//分配物理页时,水位不满足要求,触发内存回收时上报

__alloc_pages ->__alloc_pages_nodemask -> get_page_from_freelist

-> node_reclaim -> __node_reclaim -> shrink_node -> vmpressure

//分配物理页时,进入slow path,进行直接内存回收的过程中上报

__alloc_pages -> __alloc_pages_nodemask -> __alloc_pages_slowpath

-> __alloc_pages_direct_reclaim -> __perform_reclaim -> try_to_free_pages

-> do_try_to_free_pages -> shrink_zones -> shrink_node -> vmpressure

//回收cgroup内存的过程中上报

try_to_free_mem_cgroup_pages -> do_try_to_free_pages -> shrink_zones

-> shrink_node -> vmpressure

//分配物理页时,进入slow path,唤醒kswapd,kswapd在回收内存的过程中上报

kswapd -> balance_pgdat -> kswapd_shrink_node ->  shrink_node -> vmpressure

//vmpressure中进行事件上报

其中存在变量 vmpressure_win = SWAP_CLUSTER_MAX*16;

内核psi 实现


kernel-4.19/kernel/sched/psi.c 

1287  static int __init psi_proc_init(void)
1288  {
1289      proc_mkdir("pressure", NULL);
1290      proc_create("pressure/io", 0, NULL, &psi_io_fops);
1291      proc_create("pressure/memory", 0, NULL, &psi_memory_fops);
1292      proc_create("pressure/cpu", 0, NULL, &psi_cpu_fops);
1293      return 0;
1294  }
1295  module_init(psi_proc_init);


参考
https://cloud.tencent.com/developer/article/2003334

内核vmpressure

原生Linux内核是把vmpressure和CONFIG_MEMCG绑定的,是给用户态用的。

参考

linux里vm是什么,理解Linux VM pressure_星尤野的博客-CSDN博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

oom killer &lmkd killer 的相关文章

  • AUTOSAR_DCM&DEM(UDS&OBD)

    1 术语与缩写 术语 DCM Diagnostic Communication Manager DEM Diagnostic Event Manager UDS Unified diagnostic services OBD On Boar
  • Kibana学习&理解

    注 xff1a 本篇的kibana基于7 5 1版本 Kibana是什么 xff1f kibana是一个数据可视化平台 展示与分析 将es里面的东西通过各种图表展示出来 xff0c 还可以执行es的各种搜索 amp 监控 Kibana环境搭
  • shell:重启&&关机

    文章目录 shutdownhaltpoweroffrebootinitsync shutdown 关机重启命令 shutdown h 10十分钟后关机shutdown h 0马上关机shutdown h now马上关机shutdown c取
  • Lesson 9.2&9.3&9.4 黑箱:不可解释的深层神经网络&探索多层神经网络:层vsh(z)

    二 黑箱 xff1a 深层神经网络的不可解释性 首先从结构上来看 xff0c 多层神经网络比单层神经网络多出了 中间层 中间层常常被称为隐藏层 xff08 hidden layer xff09 xff0c 理论上来说可以有无限层 xff0c
  • CMake&CMakeList.txt

    1 各种关系 在各种开源项目中 xff0c 经常会发现项目中除了代码源文件 xff0c 还包含了 CMakeList txt Makefile 文件 xff0c 在项目的编译时候需要用到的命令有 cmake make 我们本次想搞清楚他们之
  • Intel RealSense L515&Unreal Engine 4调试记录

    文章目录 前言一 安装与配置1 安装前置条件2 配置 二 编译与运行1 编译2 运行 填坑与测试1 填坑2 测试 前言 Intel RealSense系列推出了适用于Unreal Engine 4的相关插件 xff0c 官网提供了相关示例代
  • 【RoboMaster】舵机驱动&蓝牙模块教程

    本文是为参加2021赛季北京理工大学机器人队校内赛所写的简单教程 xff0c 意在帮助参赛选手快速了解校内赛所需模块的使用方法 xff0c 以及其与薪火培训知识的联系 舵机驱动 硬件接线 舵机是由直流电机 减速齿轮组 传感器和控制电路组成的
  • weak & asign

    Why does Apple use assign rather than weak to store a delegate http stackoverflow com questions 20419317 why does apple
  • C++编程(五)--- Cmake详解&Makefile详解

    C C 43 43 程序员肯定离不开Makefile和Cmake xff0c 因为如果对这两个工具不熟悉 xff0c 那么你就不是一个合格的C C 43 43 程序员 本文对Makefile和Cmake xff0c 及它们的使用进行了详细的
  • STM32调试方式JTAG&SWD的区别

    在学习STM32时 xff0c 我们经常会遇到JTAG和SWD的调试方法 xff0c 还涉及到Jlink Ulink Stlink等 xff0c JTAG和SWD是ARM板一种调试模式 后面三种都是实现这种模式的一种工具 xff0c 他们之
  • 【STM32学习】——串口通信协议&STM32-USART外设&数据帧/输入数据策略/波特率发生器&串口发送/接受实操

    文章目录 前言一 串口通信1 通信接口2 串口通信 xff08 1 xff09 串口简介 xff08 2 xff09 串口硬件电路 xff08 3 xff09 串口软件部分 二 STM32的USART外设1 USART简介2 图示详解 三
  • cv::imread(cv::String const&, int)’未定义的引用

    在 Makefile文件的195 行 LIBRARIES 43 61 opencv core opencv highgui opencv imgproc 后面添加 xff1a opencv imgcodecs opencv videoio修
  • HTTP认证模式:Basic & Digest

    引言 经常在工作中使用到了各种认证方式 xff0c 但从未考虑过这些认证方式所属的知识范畴 xff0c 同时也解释不清楚它们 曾用到的认证方式 xff08 看看是否您也用过 xff0c 但很难解释清楚他们 xff09 xff1a Basic
  • Intel Realsense D435i&L515 驱动安装

    Intel Realsense D435i amp L515 驱动安装 0 引言1 D435i amp L515固件更新1 1 D435i固件更新1 2 L515固件更新 2 Intel Realsense驱动安装3 ROS Wrapper
  • linux中断&poll&selcet按键处理机制

    在上一篇linux按键中断处理中 xff0c 我们采用按键中断处理获取按键 xff0c 在read函数中阻塞读取 xff0c 当按键发生时 xff0c read自动解除阻塞 xff0c 实现应用层读取到相应的按键值 在上一节中如果没有按键到
  • ubuntu(15):对‘casadi::MX::MX(casadi::MX const&)’未定义的引用

    catkin build 编译报错 xff0c 找不到CASADI的头文件目录CASADI INCLUDE DIRS或者库文件也达不到CASADI LIBRARIES xff1b 对 casadi MX horzsplit casadi M
  • C++Primer Plus第6版&C Primer Plus第6版 中文版免费分享啦

    最近在学习C 43 43 xff0c 用的资料是师兄分享的经典书籍 C 43 43 Primer Plus第6版中文版 的PDF xff0c 自带书签 xff0c 使用很方便 但师兄说这个是他在网上花了积分才下载下来的 xff0c 这让我很
  • Linux(Centos7) 运行脚本程序,终端只返回 “已杀死”

    最近在实验室服务器上跑代码 没跑多久就显示 已杀死 而且只显示已杀死 没有任何其他打印和日志 1 确定不是代码的bug 2 网上搜了一大堆 全说是OOM的问题 就是代码消耗内存太多 被OOM killer杀死 但是我用的服务器内存确定充足
  • JVM OOM问题排查与解决思路

    OOM原因 1 堆溢出 报错信息 java lang OutOfMemoryError Java heap space 代码中可能存在大对象分配 无法获得足够的内存分配 可能发生内存泄露 导致内存被无效占用以至于耗尽 2 永久代 元空间溢出
  • 一次线上生产系统内存泄漏排查与优化实践

    今天给大家分享一个我们之前基于dubbo开发一个线上系统时候遇到的内存泄漏生产问题的排查与优化实践经验 相信对于大家多看一些类似的案例 以后对于大家自己在线上系统遇到各种生产问题的时候 进行排查和优化的思路会有很大的启发 内存泄漏问题发生背

随机推荐

  • Python pip源配置修改

    由于某些不可抗因素 xff0c Python官方的包在国内有时无法访问或出现网络不稳定现象 为了解决这个问题就需要将Pip中自带的源地址修改为镜像地址 目前收集的比较好的镜像地址有 xff1a http pypi v2ex com simp
  • 使用k-近邻算法识别手写数字。

    在之前的文章中介绍了k 近邻算法的原理知识并且用Python实现了一个分类器 xff0c 而且完成了一个简单的优化约会网站配对效果的实例 在 机器学习实战 中有关kNN的后一部分内容就是一个手写识别系统 xff0c 可以识别手写的0 9的数
  • Unable to add window -- token android.os.BinderProxy

    The problem is that the dialog need to have the 34 base 34 context of your activity not necessarily the one you 39 re la
  • HTML复选框--checkbox

    在公司实际开发中 xff0c 会经常遇到复选框全选 多选 统计选中个数的情况 xff0c 所以总结研究了一些大神的帖子 xff0c 汇总学习一下 xff01 1 统计被选中复选框的个数 2 点击复选框 xff0c 执行对应函数 3 获取复选
  • 在Ubuntu14.04不能添加PPA源到apt源的问题解决方法

    在Ubuntu14 04使用apt get 更新Git 时 xff0c 需要更新apt源 xff0c 添加一个带有最新Git的源 xff0c 如下命令 xff1a sudo add apt repository ppa git core p
  • android 12 framework开发第53节-Activity的reLaunch及onConfigurationChanged android源码分析

    hi xff0c 同学们大家好 xff01 1 Configuration应用开发背景 今天要给大家分享内容就是我们应用开发时候经常会遇到问题 xff0c 那就是如果系统一些属性变化了 xff0c 比如语言 xff0c 横竖屏幕 xff0c
  • QT 线程,实现生产者-消费者模式

    一 两种启动线程方式 xff1a 1 QThread 2 QObject moveToThread 函数 可以让 调用者的 槽中的代码 xff0c 在单独的线程执行 注 xff1a 如果该对象有 父对象 xff0c 那么它无法被移动 二 线
  • 【easyui学习笔记】3.easyui布局之边框布局

    学习参考 xff1a EasyUI 为网页创建边框布局 建设稍微实用点的界面都离不开布局 xff08 layout xff09 xff0c 我们先来了解最简单的一种布局 xff1a 边框布局 xff08 border layout xff0
  • Android的四种启动模式以及onNewIntent方法

    前言 想起来写这个是因为之前在开发过程中遇到在onStart 方法中使用getIntent 方法无法获取到启动activity时传入的数据 xff0c 也是纳闷了很久 xff0c 然后就决定好好看一下这个地方 xff0c 本文章会结合简单的
  • selenium webdriver 页面css和xpath定位

    对于html来说 xff0c 不管用什么浏览器打开 xff0c 他的架构是不变的 xff0c 所以对于编写自动化测试程序来说 xff0c 基于什么浏览器开发 xff0c 差异不大 xff0c 所以这里推荐使用chrome 65或以后版本浏览
  • Ubuntu 18.04 +Nvidia gtx 1650 显卡驱动安装

    1 Adding this PPA to your system You can update your system with unsupported packages from this untrusted PPA by adding
  • 详解https请求Nginx转发tomcat变成http问题

    概述 分享一个最近处理的nginx转发问题 xff0c 简单记录下 一 问题现象 简单架构为nginx做负载均衡 xff0c 后端用tomcat做容器 浏览器和 Nginx 之间走的 HTTPS 通讯 xff0c 而 Nginx 到 Tom
  • Zookeeper启动出现闪退问题解决

    1 检查环境变量是否配置了JAVA HOME xff0c 若是没有配置 xff0c 需配置才可以 xff08 由于zkEnv cmd文件中使用了JAVA HOME xff09 2 打开zkEnv cmd文件 xff0c 加上pause xf
  • WIN10下全新的部署和映像处理工具DISM

    DISM xff08 Deployment Image Servicing and Management xff09 就是部署映像服务和管理 DISM exe 用于安装 卸载 配置和更新脱机 Windows R 映像和脱机 Windows
  • 富文本显示不全自动省略

    使用富文本时 xff0c 文字一行 xff08 n行 xff09 显示不全 xff0c 末尾文字省略 简介 CGSize maxSize 61 CGSizeMake MAX MAXFLOAT 调整行间距 NSMutableParagraph
  • 普通用户安装管理Keepalived

    普通用户安装管理Keepalived 介绍安装授权给普通用户修改配置文件keepalived 配置检测脚本配置普通用户启动keepalived 介绍 负载均衡 Load Balance xff0c 简称LB 是一种服务或基于硬件设备等实现的
  • Arch Linux 安装小记

    Arch Linux 安装小记 这只是一篇随笔 xff0c 用来记录从全新安装 Arch Linux 开始 xff0c 到在使用过程中各种软件配置的过程 其实对于 Arch Linux 这样滚动更新的 Linux xff0c 很少需要重新安
  • Java Collections的min和max方法

    方法一 public static lt T extends Object amp Comparable lt super T gt gt T min Collection lt extends T gt coll 此方法需要传入一个实现了
  • 【高效工作】Sublime Text 3 美化

    参考 xff1a 炫酷的sublime text3主题 准备一个良好的工作环境会让人更加乐于工作 xff0c 现代的程序猿都有一颗geek的心 xff0c 谁能忍受整天在一个丑陋的UI下coding xff1f xff01 所以今天就简单介
  • oom killer &lmkd killer

    目录 oom killer amp reaper task 进程内存回收 杀进程内存回收 lmkd killer psi vmpressure 事件通知 内核psi 实现 内核vmpressure oom killer amp reaper