無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

ヒープ領域


ヒープは空いているメモリー空間から動的に獲得するものでが、ユーザメモリー空間に空きが有る限り獲得できるものでありません。ユーザプロセスで使用するmalloc関数は、glibcで実装されており、すなわちユーザプロセスとして実装されています。ヒープ処理としてカーネルはそのヒープと使えるメモリー空間を提供するのみで、ヒープ領域を設定するシステムコールはbrk関数です。

まず、min_brkにヒープ開始アドレスをセットしています。CONFIG_COMPAT_BRKでそのアドレスをmm->end_code(実行コードの終端アドレス)か、mm->start_brk(ヒープ開始アドレス)としているようです。そして拡張するアドレスがそれより小さければエラーです。

rlim = current->signal->rlim[RLIMIT_DATA].rlim_curで、プロセスのデータのリソースの拡張が可能で、ヒープ領域とデータ領域の合計が、rlimより小さいことを確認しています。

PAGE_ALIGNマクロは指定されたアドレスを、ページ単位アライメントします。拡張するページアライメントされたアドレスをnewbrkに、元のアドレスを oldbrk に設定します。

ヒープ領域を縮小する場合if (brk <= mm->brk)、do_munmapで縮小し、縮小したアドレスをmm->brk = brkとしています。拡張の場合、find_vma_intersection関数で実際に拡張できるかチェックします。これはnoldbrk, newbrk+PAGE_SIZE分拡張しても、ほかのメモリーリージョンと重ならないか(重なるというのはすでに利用されている。)チェックします。

すべてOKならdo_brkで無名メモリーリージョンとして確保し、mm->brk = brkとしています。

root/mm/mmap.c

SYSCALL_DEFINE1(brk, unsigned long, brk)
{
       unsigned long rlim, retval;
       unsigned long newbrk, oldbrk;
       struct mm_struct *mm = current->mm;
       unsigned long min_brk;

       down_write(&mm->mmap_sem);
 
#ifdef CONFIG_COMPAT_BRK
       min_brk = mm->end_code;
#else
       min_brk = mm->start_brk;
#endif
       if (brk < min_brk)
               goto out;

       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
       if (rlim < RLIM_INFINITY && (brk - mm->start_brk) +
                       (mm->end_data - mm->start_data) > rlim)
               goto out;

       newbrk = PAGE_ALIGN(brk);
       oldbrk = PAGE_ALIGN(mm->brk);
       if (oldbrk == newbrk)
               goto set_brk;

       if (brk <= mm->brk) {
               if (!do_munmap(mm, newbrk, oldbrk-newbrk))
                       goto set_brk;
               goto out;
       }

       if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
               goto out;

       if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
               goto out;
set_brk:
       mm->brk = brk;
out:
       retval = mm->brk;
       up_write(&mm->mmap_sem);
       return retval;
}
要は拡張するリニアアドレス分のメモリーリージョンとして確保して、そのアドレスをmm->brkにセットするということです。従ってヒープというのは空いているメモリー空間からでなく、mm->start_brkからmm->brkの連続する範囲で確保されるということです。

そこで、以下は詳解LINUXカーネルの柔軟なメモリーリージョンの割付という項目の焼き直しです。ユーザプロセスにスタックの大きさを付けて動作させた場合と、そうでない場合とで、ファイルマッピングメモリーリージョンおよび無名メモリーリージョンの配置が異なると言うものです。このヒープ領域を有効に取得できるようにするためだそうです。

下記のプログラムをスタックの大きさに制限をした場合と、そうでない場合のメモリーリージョンの配置を見てみたいと思います。
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
   void mymmap();
   mymmap();
   while(1)
       sleep(100);
}
void mymmap()
{
  int fd = open("a.c",O_RDWR);
  char *p = mmap(0,10,PROT_READ,MAP_SHARED,fd,0);
}
スタックに制限を付けないで実行した結果です。a.cをmmapでメモリーマップしたアドレスが40002000-40003000となっています。これらのメモリーリージョンは40000000番地から割り当てられるようになっているようです。そしてヒープは実行コードの後ろの083bb000-083dc000として割り当てられています。結果として、スタック制限を付けない場合、ヒープはコード領域の終了から40000000番地(1G)までの範囲でか確保できません。
[root@localhost kitamura]# ulimit -s unlimited; ./a.out &
[1] 24485
[root@localhost kitamura]# cat /proc/24485/maps 
00522000-00542000 r-xp 00000000 fd:00 10553      /lib/ld-2.9.so
00543000-00544000 r--p 00020000 fd:00 10553      /lib/ld-2.9.so
00544000-00545000 rw-p 00021000 fd:00 10553      /lib/ld-2.9.so
00547000-006b5000 r-xp 00000000 fd:00 10554      /lib/libc-2.9.so
006b5000-006b7000 r--p 0016e000 fd:00 10554      /lib/libc-2.9.so
006b7000-006b8000 rw-p 00170000 fd:00 10554      /lib/libc-2.9.so
006b8000-006bb000 rw-p 006b8000 00:00 0 
08048000-08049000 r-xp 00000000 fd:00 139411     /home/kitamura/a.out
08049000-0804a000 rw-p 00000000 fd:00 139411     /home/kitamura/a.out
083bb000-083dc000 rw-p 083bb000 00:00 0          [heap]
40000000-40001000 r-xp 40000000 00:00 0          [vdso]
40001000-40002000 rw-p 40001000 00:00 0 
40002000-40003000 r--s 00000000 fd:00 139603     /home/kitamura/a.c
40011000-40013000 rw-p 40011000 00:00 0 
bfac9000-bfade000 rw-p bffeb000 00:00 0          [stack]
今度はスタックに制限をつけて動作させると、a.cをmmapでメモリーマップしたアドレスがb78cd000-b78ce000となっていて、ちょうどスタックの上から配置されるようです。ヒープは実行コードの後ろの08af8000-08b19000に割り当てられています。この場合ヒープは実行コードの後ろから上位に向かって、スタックおよびメモリーマップ等(下位に向かって伸びる。)で使用していないメモリー空間すべてを確保することができると言うことです。
[root@localhost kitamura]# ulimit -s 100; ./a.out &
[1] 24481
[root@localhost kitamura]# cat /proc/24481/maps 
00522000-00542000 r-xp 00000000 fd:00 10553      /lib/ld-2.9.so
00543000-00544000 r--p 00020000 fd:00 10553      /lib/ld-2.9.so
00544000-00545000 rw-p 00021000 fd:00 10553      /lib/ld-2.9.so
00547000-006b5000 r-xp 00000000 fd:00 10554      /lib/libc-2.9.so
006b5000-006b7000 r--p 0016e000 fd:00 10554      /lib/libc-2.9.so
006b7000-006b8000 rw-p 00170000 fd:00 10554      /lib/libc-2.9.so
006b8000-006bb000 rw-p 006b8000 00:00 0 
009e1000-009e2000 r-xp 009e1000 00:00 0          [vdso]
08048000-08049000 r-xp 00000000 fd:00 139411     /home/kitamura/a.out
08049000-0804a000 rw-p 00000000 fd:00 139411     /home/kitamura/a.out
08af8000-08b19000 rw-p 08af8000 00:00 0          [heap]
b78bd000-b78bf000 rw-p b78bd000 00:00 0 
b78cd000-b78ce000 r--s 00000000 fd:00 139603     /home/kitamura/a.c
b78ce000-b78cf000 rw-p b78ce000 00:00 0 
bfe9b000-bfeb0000 rw-p bffeb000 00:00 0          [stack]

最終更新 2010/11/13 20:09:24 - north
(2010/11/13 20:09:24 作成)