织梦CMS - 轻松建站从此开始!

罗索

当前位置: 主页>嵌入式开发>Android>

Android Low Memory Killer

jackyhwei 发布于 2020-10-22 15:49 点击:次 
Android的Low Memory Killer基于Linux的OOM机制,在Linux中,内存是以页面为单位分配的,当申请页面分配时如果内存不足会通过以下流程选择bad进程来杀掉从而释放内存
TAG: 进程回收  LMK  

Low Memory Killer的原理

  在Android中,即使当用户退出应用程序之后,应用程序的进程也还是存在于系统中,这样是为了方便程序的再次启动,但是这样的话,随着打开的程序数量的增加,系统的内存会变得不足,就需要杀掉一部分进程以释放内存空间。至于是否需要杀死一些进程和哪些进程需要被杀死,是通过Low Memory Killer机制来进行判定的。

  Android的Low Memory Killer基于Linux的OOM机制,在Linux中,内存是以页面为单位分配的,当申请页面分配时如果内存不足会通过以下流程选择bad进程来杀掉从而释放内存:

alloc_pages -> out_of_memory() -> select_bad_process() -> badness()

  在Low Memory Killer中通过进程的oom_adj与占用内存的大小决定要杀死的进程,oom_adj越小越不容易被杀死。

  Low Memory Killer Driver在用户空间指定了一组内存临界值及与之一一对应的一组oom_adj值,当系统剩余内存位于内存临界值中的一个范围内时,如果一个进程的oom_adj值大于或等于这个临界值对应的oom_adj值就会被杀掉。

  可以通过修改/sys/module/lowmemorykiller/parameters/minfree与/sys/module/lowmemorykiller/parameters/adj来改变内存临界值及与之对应的oom_adj值。minfree中数值的单位是内存中的页面数量,一般情况下一个页面是4KB。
比如如果向/sys/module/lowmemorykiller/parameters/adj写入0,8,向/sys/module/lowmemorykiller/parameters/minfree中写入1024,4096,假设一个页面大小为4KB,这样当系统空闲内存位于1024*4~4096*4KB之间时oom_adj大于等于8的进程就会被杀掉。

  在lowmemorykiller.c中定义了阈值表的默认值,可以通过init.rc自定义:

  1. static int lowmem_adj[6] = { 
  2.         0, 
  3.         1, 
  4.         6, 
  5.         12, 
  6. }; 
  7. static int lowmem_adj_size = 4
  8. static size_t lowmem_minfree[6] = { 
  9.         3 * 512,        /* 6MB */ 
  10.         2 * 1024,       /* 8MB */ 
  11.         4 * 1024,       /* 16MB */ 
  12.         16 * 1024,      /* 64MB */ 
  13. }; 
  14. static int lowmem_minfree_size = 4

在init.rc中定义了init进程的oom_adj为-16,不可能会被杀死(init的PID是1):

  1. on early-init 
  2.     # Set init and its forked children's oom_adj. 
  3.     write /proc/1/oom_adj -16 

在Linux中有一个kswapd的内核线程,当linux回收内存分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,定义如下:

  1. /* 
  2.  * A callback you can register to apply pressure to ageable caches. 
  3.  * 
  4.  * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'.  It should 
  5.  * look through the least-recently-used 'nr_to_scan' entries and 
  6.  * attempt to free them up.  It should return the number of objects 
  7.  * which remain in the cache.  If it returns -1, it means it cannot do 
  8.  * any scanning at this time (eg. there is a risk of deadlock). 
  9.  * 
  10.  * The 'gfpmask' refers to the allocation we are currently trying to 
  11.  * fulfil. 
  12.  * 
  13.  * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is 
  14.  * querying the cache size, so a fastpath for that case is appropriate. 
  15. */ 
  16. struct shrinker { 
  17.     int (*shrink)(int nr_to_scan, gfp_t gfp_mask); 
  18.     int seeks;      /* seeks to recreate an obj */ 
  19.  
  20.     /* These are for internal use */ 
  21.     struct list_head list; 
  22.     long nr;        /* objs pending delete */ 
  23. }; 
  24. #define DEFAULT_SEEKS 2 /* A good number if you don't know better. */ 
  25. extern void register_shrinker(struct shrinker *); 
  26. extern void unregister_shrinker(struct shrinker *); 

通过register_shrinker与unregister_shrinker向shrinker链表中添加或移除回调。当注册Shrinker后就可以在回收内存分页时按自己定义的规则释放内存。

Android Low Memory Killer的代码在drivers/staging/android/lowmemorykiller.c中,通过以下代码在模块初始化时注册Shrinker:

  1. static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask); 
  2.   
  3. static struct shrinker lowmem_shrinker = { 
  4.         .shrink = lowmem_shrink
  5.         .seeks = DEFAULT_SEEKS * 16 
  6. }; 
  7.  
  8. static int __init lowmem_init(void) 
  9.         register_shrinker(&lowmem_shrinker); 
  10.         return 0; 
  11.  
  12. static void __exit lowmem_exit(void) 
  13.         unregister_shrinker(&lowmem_shrinker); 
  14.  
  15. module_init(lowmem_init); 
  16. module_exit(lowmem_exit); 

这样就可以在回收内存分页时调用lowmem_shrink函数。

Low Memory Killer的实现

  lowmem_shrink的定义如下:

  1. static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) 
  2.         struct task_struct *p; 
  3.         struct task_struct *selected = NULL
  4.         int rem = 0
  5.         int tasksize; 
  6.         int i; 
  7.         int min_adj = OOM_ADJUST_MAX + 1; 
  8.         int selected_tasksize = 0
  9.         int selected_oom_adj; 
  10.         int array_size = ARRAY_SIZE(lowmem_adj); 
  11.         int other_free = global_page_state(NR_FREE_PAGES); 
  12.         int other_file = global_page_state(NR_FILE_PAGES); 
  13.  
  14.         if (lowmem_adj_size < array_size
  15.                 array_size = lowmem_adj_size
  16.         if (lowmem_minfree_size < array_size
  17.                 array_size = lowmem_minfree_size
  18.         for (i = 0; i < array_size; i++) { 
  19.                 if (other_free < lowmem_minfree[i] && 
  20.                     other_file < lowmem_minfree[i]) { 
  21.                         min_adj = lowmem_adj[i]; 
  22.                         break; 
  23.                 } 
  24.         } 
  25.         if (nr_to_scan > 0) 
  26.                 lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", 
  27.                              nr_to_scan, gfp_mask, other_free, other_file, 
  28.                              min_adj); 
  29.         rem = global_page_state(NR_ACTIVE_ANON) + 
  30.                 global_page_state(NR_ACTIVE_FILE) + 
  31.                 global_page_state(NR_INACTIVE_ANON) + 
  32.                 global_page_state(NR_INACTIVE_FILE); 
  33.         if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { 
  34.                 lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", 
  35.                              nr_to_scan, gfp_mask, rem); 
  36.                 return rem; 
  37.         } 
  38.         selected_oom_adj = min_adj
  39.  
  40.         read_lock(&tasklist_lock); 
  41.         for_each_process(p) { 
  42.                 struct mm_struct *mm; 
  43.                 int oom_adj; 
  44.  
  45.                 task_lock(p); 
  46.                 mm = p->mm; 
  47.                 if (!mm) { 
  48.                         task_unlock(p); 
  49.                         continue; 
  50.                 } 
  51.                 oom_adj = mm->oom_adj; 
  52.                 if (oom_adj < min_adj) { 
  53.                         task_unlock(p); 
  54.                         continue; 
  55.                 } 
  56.                 tasksize = get_mm_rss(mm); 
  57.                 task_unlock(p); 
  58.                 if (tasksize <= 0) 
  59.                         continue; 
  60.                 if (selected) { 
  61.                         if (oom_adj < selected_oom_adj
  62.                                 continue; 
  63.                         if (oom_adj == selected_oom_adj && 
  64.                             tasksize <= selected_tasksize) 
  65.                                 continue; 
  66.                 } 
  67.                 selected = p
  68.                 selected_tasksize = tasksize; 
  69.                 selected_oom_adj = oom_adj; 
  70.                 lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", 
  71.                              p->pid, p->comm, oom_adj, tasksize); 
  72.         } 
  73.         if (selected) { 
  74.                 lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", 
  75.                              selected->pid, selected->comm, 
  76.                              selected_oom_adj, selected_tasksize); 
  77.                 force_sig(SIGKILL, selected); 
  78.                 rem -selected_tasksize
  79.         } 
  80.         lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", 
  81.                      nr_to_scan, gfp_mask, rem); 
  82.         read_unlock(&tasklist_lock); 
  83.         return rem; 

分开来看这段代码,首先取得内存阈值表的大小,取阈值表数组大小与lowmem_adj_size,lowmem_minfree_size的较小值,然后通过globa_page_state获得当前剩余内存的大小,然后跟内存阈值表中的阈值相比较获得min_adj与selected_oom_adj:

  1. int array_size = ARRAY_SIZE(lowmem_adj); 
  2. int other_free = global_page_state(NR_FREE_PAGES); 
  3. int other_file = global_page_state(NR_FILE_PAGES); 
  4.  
  5. if (lowmem_adj_size < array_size
  6.         array_size = lowmem_adj_size
  7. if (lowmem_minfree_size < array_size
  8.         array_size = lowmem_minfree_size
  9. for (i = 0; i < array_size; i++) { 
  10.     if (other_free < lowmem_minfree[i] && other_file < lowmem_minfree[i]) { 
  11.          min_adj = lowmem_adj[i]; 
  12.          break; 
  13.     } 
  14. }selected_oom_adj = min_adj

遍历所有进程找到oom_adj>min_adj并且占用内存大的进程:

  1. read_lock(&tasklist_lock); 
  2. for_each_process(p) { 
  3.     struct mm_struct *mm; 
  4.     int oom_adj; 
  5.  
  6.     task_lock(p); 
  7.     mm = p->mm; 
  8.     if (!mm) { 
  9.         task_unlock(p); 
  10.         continue; 
  11.     } 
  12.     oom_adj = mm->oom_adj; 
  13.     //获取task_struct->struct_mm->oom_adj,如果小于警戒值min_adj不做处理 
  14.     if (oom_adj < min_adj) { 
  15.         task_unlock(p); 
  16.         continue; 
  17.     } 
  18.     //如果走到这里说明oom_adj>=min_adj,即超过警戒值 
  19.     //获取内存占用大小,若<=0,不做处理 
  20.     tasksize = get_mm_rss(mm); 
  21.     task_unlock(p); 
  22.     if (tasksize <= 0) 
  23.         continue; 
  24.     //如果之前已经先择了一个进程,比较当前进程与之前选择的进程的oom_adj与内存占
  25. //用大小,如果oom_adj比之前选择的小或相等而内存占用比之前选择的进程小,不做处理。 
  26.     if (selected) { 
  27.         if (oom_adj < selected_oom_adj
  28.             continue; 
  29.         if (oom_adj == selected_oom_adj && 
  30.             tasksize <= selected_tasksize) 
  31.             continue; 
  32.     } 
  33.     //走到这里表示当前进程比之前选择的进程oom_adj大或相等但占用内存大,选择当前进程 
  34.     selected = p
  35.     selected_tasksize = tasksize; 
  36.     selected_oom_adj = oom_adj; 
  37.     lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", 
  38.                  p->pid, p->comm, oom_adj, tasksize); 

如果选择出了符合条件的进程,发送SIGNAL信号Kill掉:

  1. if (selected) { 
  2.     lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", 
  3.                  selected->pid, selected->comm, 
  4.                  selected_oom_adj, selected_tasksize); 
  5.     force_sig(SIGKILL, selected); 
  6.     rem -selected_tasksize

oom_adj与上层Process Importance的关系

  我们知道,在上层进程按重要性可以分为:Foreground process,Visible process,Service process,Background process与Empty process,那么这些重要性怎么与Low Memory Killer中的oom_adj对应起来的呢?

  在ActivityManager.RunningAppProcessInfo中我们可以看到如下关于importance的定义:

  1. /** 
  2.  * Constant for {@link #importance}: this is a persistent process. 
  3.  * Only used when reporting to process observers. 
  4.  * @hide 
  5.  */ 
  6. public static final int IMPORTANCE_PERSISTENT = 50
  7.  
  8. /** 
  9.  * Constant for {@link #importance}: this process is running the 
  10.  * foreground UI. 
  11.  */ 
  12. public static final int IMPORTANCE_FOREGROUND = 100
  13.  
  14. /** 
  15.  * Constant for {@link #importance}: this process is running something 
  16.  * that is actively visible to the user, though not in the immediate 
  17.  * foreground. 
  18.  */ 
  19. public static final int IMPORTANCE_VISIBLE = 200
  20.  
  21. /** 
  22.  * Constant for {@link #importance}: this process is running something 
  23.  * that is considered to be actively perceptible to the user.  An 
  24.  * example would be an application performing background music playback. 
  25.  */ 
  26. public static final int IMPORTANCE_PERCEPTIBLE = 130
  27.  
  28. /** 
  29.  * Constant for {@link #importance}: this process is running an 
  30.  * application that can not save its state, and thus can't be killed 
  31.  * while in the background. 
  32.  * @hide 
  33.  */ 
  34. public static final int IMPORTANCE_CANT_SAVE_STATE = 170
  35.  
  36. /** 
  37.  * Constant for {@link #importance}: this process is contains services 
  38.  * that should remain running. 
  39.  */ 
  40. public static final int IMPORTANCE_SERVICE = 300
  41.  
  42. /** 
  43.  * Constant for {@link #importance}: this process process contains 
  44.  * background code that is expendable. 
  45.  */ 
  46. public static final int IMPORTANCE_BACKGROUND = 400
  47.  
  48. /** 
  49.  * Constant for {@link #importance}: this process is empty of any 
  50.  * actively running code. 
  51.  */ 
  52. public static final int IMPORTANCE_EMPTY = 500

这些常量表示了Process的Importance等级,而在ProcessList中我们会发现关于adj的一些定义:

  1. // This is a process only hosting activities that are not visible, 
  2. // so it can be killed without any disruption. 
  3. static final int HIDDEN_APP_MAX_ADJ = 15
  4. static int HIDDEN_APP_MIN_ADJ = 9
  5.  
  6. // The B list of SERVICE_ADJ -- these are the old and decrepit 
  7. // services that aren't as shiny and interesting as the ones in the A list. 
  8. static final int SERVICE_B_ADJ = 8
  9.  
  10. // This is the process of the previous application that the user was in. 
  11. // This process is kept above other things, because it is very common to 
  12. // switch back to the previous app.  This is important both for recent 
  13. // task switch (toggling between the two top recent apps) as well as normal 
  14. // UI flow such as clicking on a URI in the e-mail app to view in the browser, 
  15. // and then pressing back to return to e-mail. 
  16. static final int PREVIOUS_APP_ADJ = 7
  17.  
  18. // This is a process holding the home application -- we want to try 
  19. // avoiding killing it, even if it would normally be in the background, 
  20.  
  21. // because the user interacts with it so much. 
  22. static final int HOME_APP_ADJ = 6
  23.  
  24. // This is a process holding an application service -- killing it will not 
  25. // have much of an impact as far as the user is concerned. 
  26. static final int SERVICE_ADJ = 5
  27.  
  28. // This is a process currently hosting a backup operation.  Killing it 
  29. // is not entirely fatal but is generally a bad idea. 
  30. static final int BACKUP_APP_ADJ = 4
  31.  
  32. // This is a process with a heavy-weight application.  It is in the 
  33. // background, but we want to try to avoid killing it.  Value set in 
  34. // system/rootdir/init.rc on startup. 
  35. static final int HEAVY_WEIGHT_APP_ADJ = 3
  36.  
  37. // This is a process only hosting components that are perceptible to the 
  38. // user, and we really want to avoid killing them, but they are not 
  39. // immediately visible. An example is background music playback. 
  40. static final int PERCEPTIBLE_APP_ADJ = 2
  41.  
  42. // This is a process only hosting activities that are visible to the 
  43. // user, so we'd prefer they don't disappear. 
  44. static final int VISIBLE_APP_ADJ = 1
  45.  
  46. // This is the process running the current foreground app.  We'd really 
  47. // rather not kill it! 
  48. static final int FOREGROUND_APP_ADJ = 0
  49.  
  50. // This is a system persistent process, such as telephony.  Definitely 
  51. // don't want to kill it, but doing so is not completely fatal. 
  52. static final int PERSISTENT_PROC_ADJ = -12; 
  53.  
  54. // The system process runs at the default adjustment. 
  55. static final int SYSTEM_ADJ = -16; 

我们可以看到:

  1. static final int PREVIOUS_APP_ADJ = 7
  2. static final int HOME_APP_ADJ = 6

并不是所有的Background process的等级都是相同的。

关于ADJ与Importance的值都找到了,那么它们是怎么对应起来的呢?Activity实际是由ActivityManagerService来管理的,在ActivityManagerService中我们可以找到以下函数:

  1. static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) { 
  2.     if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) { 
  3.         if (currApp != null) { 
  4.             currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1; 
  5.         } 
  6.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; 
  7.     } else if (adj >= ProcessList.SERVICE_B_ADJ) { 
  8.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; 
  9.     } else if (adj >= ProcessList.HOME_APP_ADJ) { 
  10.         if (currApp != null) { 
  11.             currApp.lru = 0
  12.         } 
  13.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; 
  14.     } else if (adj >= ProcessList.SERVICE_ADJ) { 
  15.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; 
  16.     } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) { 
  17.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE; 
  18.     } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) { 
  19.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE; 
  20.     } else if (adj >= ProcessList.VISIBLE_APP_ADJ) { 
  21.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 
  22.     } else { 
  23.         return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 
  24.     } 

在这个函数中实现了根据adj设置importance的功能。

我们还可以看到SERVICE还分为SERVICE_B_ADJ与SERVICE_ADJ,等级是不一样的,并不是所有Service的优先级都比Background process的优先级高。当调用Service的startForeground后,Service的importance就变为了IMPORTANCE_PERCEPTIBLE(在记忆中曾经将Service设置为foreground并打印出其importance的值与IMPORTANCE_PERCEPTIBLE相等),对应的adj是PERCEPTIBLE_APP_ADJ,即2,已经很难被系统杀死了。

  1. // This is a system persistent process, such as telephony.  Definitely 
  2. // don't want to kill it, but doing so is not completely fatal. 
  3. static final int PERSISTENT_PROC_ADJ = -12; 
  4.  
  5. // The system process runs at the default adjustment. 
  6. static final int SYSTEM_ADJ = -16; 

像电话等进程的adj为-12已基本不可能被杀死了,而在前面已经看到了,init.rc中将init进程的oom_adj设置为了-16,已经是永生进程了。

相关链接:

lowermemorykiller.txt

lowermemorykiller.c

shrinker_list


 

(angeldevil)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/202010/17758.html]
本文出处:博客园 作者:angeldevil 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容