glibcのmalloc(3)とLinuxカーネルのovercommitとOOM

この記事はLinux Advent Calendar 2015の23日目です。

glibcmalloc(3)とLinuxカーネルのovercommitとOOMの関連でも見てみようと思います。Linuxカーネルシステムコールにはmalloc(3)的なものはないので、mmap(2)、brk(2)が使われます。 今回知りたかったのは、どこでovercommitを許可しているのか?というところと、どのタイミングでOOMが発生するのかという2点です。

OOM発生時の挙動についてはsatoru_takeuchiさんの覚書(2015-12-03)に詳しく書かれていますので、こちらを参照してください。 glibcmalloc(3)に関してはがちゃぴん先生のmalloc動画を見ましょう。

今回見ているglibcのバージョンは2.22でカーネルは4.3です。

まずはmalloc(3)の大まかな処理を見ます。

malloc(3)を呼ぶとglibc__libc_malloc()が呼ばれます。 これはmalloc/malloc.cの下のほうでaliasが設定されています。

__libc_malloc()の全容はこのようになっています。

2894 void *
2895 __libc_malloc (size_t bytes)
2896 {
2897   mstate ar_ptr;
2898   void *victim;
2899 
2900   void *(*hook) (size_t, const void *)
2901     = atomic_forced_read (__malloc_hook);
2902   if (__builtin_expect (hook != NULL, 0))
2903     return (*hook)(bytes, RETURN_ADDRESS (0));
2904 
2905   arena_get (ar_ptr, bytes);
2906 
2907   victim = _int_malloc (ar_ptr, bytes);
2908   /* Retry with another arena only if we were able to find a usable arena
2909      before.  */
2910   if (!victim && ar_ptr != NULL)
2911     {
2912       LIBC_PROBE (memory_malloc_retry, 1, bytes);
2913       ar_ptr = arena_get_retry (ar_ptr, bytes);
2914       victim = _int_malloc (ar_ptr, bytes);
2915     }
2916 
2917   if (ar_ptr != NULL)
2918     (void) mutex_unlock (&ar_ptr->mutex);
2919 
2920   assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
2921           ar_ptr == arena_for_chunk (mem2chunk (victim)));
2922   return victim;
2923 }
2924 libc_hidden_def (__libc_malloc)

単純に言うと、_int_malloc()で要求されたメモリが確保できればOK。できなかったら、arena_get_retry()でarenaを変えてからもう一度チャレンジというのがmalloc(3)の大雑把な挙動になってます。

_int_malloc()はそれなりに長い関数なのでさくっと流したいところです。

3311 static void *
3312 _int_malloc (mstate av, size_t bytes)
~~~
3345   /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
3346      mmap.  */
3347   if (__glibc_unlikely (av == NULL))
3348     {
3349       void *p = sysmalloc (nb, av);
3350       if (p != NULL)
3351         alloc_perturb (p, bytes);
3352       return p;
3353     }

__libc_malloc()でarena_get()を呼んで取得したarenaがNULLということは、使用可能なarenaが無いということで、sysmalloc()というのが呼ばれます。

また、_int_malloc()の最後のほうでもarenaからではなく、OS側にメモリを要求する必要があるときにsysmalloc()を呼んでいます。

3816       /*
3817          Otherwise, relay to handle system-dependent cases
3818        */
3819       else
3820         {
3821           void *p = sysmalloc (nb, av);
3822           if (p != NULL)
3823             alloc_perturb (p, bytes);
3824           return p;
3825         }

では、sysmalloc()をさくっと見ていきます。

2260 static void *
2261 sysmalloc (INTERNAL_SIZE_T nb, mstate av)
2262 {

基本的にmmap(2)もしくはsbrk(2)を使うという感じです。 OSがmmapをサポートしていて、要求されたサイズがmmapで確保する場合のしきい値を超えてないなんて場合にmmap(2)でメモリの確保をしにいきます。

2286   /*
2287      If have mmap, and the request size meets the mmap threshold, and
2288      the system supports mmap, and there are few enough currently
2289      allocated mmapped regions, try to directly map this request
2290      rather than expanding top.
2291    */
2292 

mmap(2)に失敗し、使用可能なarenaも無ければ終了です。

2368   /* There are no usable arenas and mmap also failed.  */
2369   if (av == NULL)
2370     return 0;

次にメモリを確保しようとしたarenaの種別によって処理が変わっています。

2394   if (av != &main_arena)
2395     {
~~~
2445   else     /* av == main_arena */

まず、av != &main_arenaの場合。heapを拡張する(grow_heap())、heapを新しく作る(new_heap())、mmapするのどれかになります。

2402       if ((long) (MINSIZE + nb - old_size) > 0
2403           && grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
~~~
2410       else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
~~~
2441       else if (!tried_mmap)
2442         /* We can at least try to use to mmap memory.  */
2443         goto try_mmap

main_arenaの場合は、sbrk(2)でデータセグメントを拡張し、

2476       if (size > 0)
2477         {
2478           brk = (char *) (MORECORE (size));
2479           LIBC_PROBE (memory_sbrk_more, 2, brk, size);
2480         }

これに失敗したらmmapを試します。

/* Don't try if size wraps around 0 */
2509           if ((unsigned long) (size) > (unsigned long) (nb))
2510             {
2511               char *mbrk = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));
2512 

大雑把に見ていくとsysmalloc()はだいたいこのような流れです。

このほかarenaを作るというときもあって、これは_int_new_arena()で行います。メモリを確保する処理はnew_heap()ですね。

 722 /* Create a new arena with initial size "size".  */
 723 
 724 static mstate
 725 _int_new_arena (size_t size)
 726 {
 727   mstate a;
 728   heap_info *h;
 729   char *ptr;
 730   unsigned long misalign;
 731 
 732   h = new_heap (size + (sizeof (*h) + sizeof (*a) + MALLOC_ALIGNMENT),
 733                 mp_.top_pad);

new_heap()はmmap(2)を使います。

どんなときにarenaを作っているかというと例えば、arena_get_retry()でメモリを確保しようとしたarenaがmain_arenaではない場合、arena_get2()を呼んで、どこも空いてなければなんて時です。

 887       if (__glibc_unlikely (n <= narenas_limit - 1))
 888         {
 889           if (catomic_compare_and_exchange_bool_acq (&narenas, n + 1, n))
 890             goto repeat;
 891           a = _int_new_arena (size);
 892           if (__glibc_unlikely (a == NULL))
 893             catomic_decrement (&narenas);
 894         }
 895       else
 896         a = reused_arena (avoid_arena);

ここまでがglibcの処理でした。ではカーネル側を見てきましょう。

まずは、overcommitの設定を見ている関数ですが、これは__vm_enough_memory()です。 vm.overcommit_memory(/proc/sys/vm/overcommit_memory)の値を見てovercommit可能なら0を、できないようなら-ENOMEMを返します。overcommitができない場合というのは、設定で許可していない場合とovercommit可能かどうか計算した結果だめだった場合の2パターンです。 __vm_enough_memory()は直接使用されていなくて、security_vm_enough_memory_mm()という関数から呼ばれます。

security_vm_enough_memory_mm()を呼んでいる箇所はいくつかあります。glibcmalloc(3)がbrk(2)、mmap(2)を使っているのでその辺りをチェックするとやはり読んでいます。

do_brk()ではこのように使っています。

2766         if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT))
2767                 return -ENOMEM;

mmapの場合はmmap_region()にて使用しています。

1572         /*
1573          * Private writable mapping: check memory availability
1574          */
1575         if (accountable_mapping(file, vm_flags)) {
1576                 charged = len >> PAGE_SHIFT;
1577                 if (security_vm_enough_memory_mm(mm, charged))
1578                         return -ENOMEM;
1579                 vm_flags |= VM_ACCOUNT;
1580         }

mmap_region()do_mmap()の最後のほうで呼んでいます。mmap_region()の返り値がmmap(2)の返り値ですね。

1402         addr = mmap_region(file, addr, len, vm_flags, pgoff);
1403         if (!IS_ERR_VALUE(addr) &&
1404             ((vm_flags & VM_LOCKED) ||
1405              (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
1406                 *populate = len;
1407         return addr;

というわけで、brk(2)、mmap(2)ともにsecurity_vm_enough_memory_mm()を呼んで、overcommit可能か調べて可能ならメモリを確保します。

do_brk()の場合はこのようになってます。すでに使える領域があればそれを、なければvma構造体を新規に作ります。mmap_region()も同じ感じです。

2769         /* Can we just expand an old private anonymous mapping? */
2770         vma = vma_merge(mm, prev, addr, addr + len, flags,
2771                         NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX);
2772         if (vma)
2773                 goto out;
2774 
2775         /*
2776          * create a vma struct for an anonymous mapping
2777          */
2778         vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
2779         if (!vma) {
2780                 vm_unacct_memory(len >> PAGE_SHIFT);
2781                 return -ENOMEM;
2782         }
~~~
2785         vma->vm_mm = mm;
2786         vma->vm_start = addr;
2787         vma->vm_end = addr + len;
2788         vma->vm_pgoff = pgoff;
2789         vma->vm_flags = flags;
2790         vma->vm_page_prot = vm_get_page_prot(flags);
2791         vma_link(mm, vma, prev, rb_link, rb_parent);

最後にメモリが足りなくなった時の処理を。 メモリが足りなくなった場合の最終的な処理はcheck_panic_on_oom()でpanicするか、check_panic_on_oom()を呼んだ側でプロセスをkillするかです。 check_panic_on_oom()これを呼んでいるところは2箇所あります。

1つはout_of_memory()

675         constraint = constrained_alloc(oc, &totalpages);
676         if (constraint != CONSTRAINT_MEMORY_POLICY)
677                 oc->nodemask = NULL;
678         check_panic_on_oom(oc, constraint, NULL);

もうひとつはmem_cgroup_out_of_memory()

1332         if (fatal_signal_pending(current) || task_will_free_mem(current)) {
1333                 mark_oom_victim(current);
1334                 goto unlock;
1335         }
1336 
1337         check_panic_on_oom(&oc, CONSTRAINT_MEMCG, memcg);
1338         totalpages = mem_cgroup_get_limit(memcg) ? : 1;

out_of_memory()を呼ぶ流れとしては、下記の流れが基本っぽいです。

__alloc_pages_nodemask()
  -> __alloc_pages_slowpath()
    -> __alloc_pages_may_oom()
      -> out_of_memory()

これはpageを確保しようとして失敗したケース。

次にmem_cgroup_out_of_memory()が呼ばれる流れはこうです。do_page_fault()と__do_page_fault()はアーキテクチャ依存の関数です。

do_page_fault()
  -> __do_page_fault()
    -> mm_fault_error()
      -> pagefault_out_of_memory()
        -> mem_cgroup_oom_synchronize()
          -> mem_cgroup_out_of_memory()

まず、__alloc_pages_nodemask()でOOMが発生するパターンですが、これは例えば、kmem_cache_zalloc()を呼んだ時に空いているslabオブジェクトがなかったので、slabを作るためにpageの新規確保をしに行ったけど(slowpath)pageすら確保できなくてOOMというパターンでしょうか。

ページフォルトの場合はpageの割りあてが必要だったけど、できなかったケースですね。pagefault_out_of_memory()に説明が書かれています。

707 /*
708  * The pagefault handler calls here because it is out of memory, so kill a
709  * memory-hogging task.  If any populated zone has ZONE_OOM_LOCKED set, a
710  * parallel oom killing is already in progress so do nothing.
711  */
712 void pagefault_out_of_memory(void)

というわけで、malloc(3)とovercommitの許可、OOMの発生なんかを見てきました( ´ー`)フゥー...