トップ «前の日記(2004年02月19日) 最新 次の日記(2004年02月21日)» 編集
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|10|12|
2009|02|03|06|07|10|11|12|
2010|01|02|03|04|07|09|10|11|12|
2011|01|03|04|05|06|07|08|10|
2012|01|06|08|09|10|12|
2013|01|02|03|04|07|09|11|12|
2014|01|03|04|05|06|09|
2015|04|
2016|01|08|
ここは旧えびめもです。えびめも2に移行します(2016/12/1)

2004年02月20日

CAT709

linux-2.6カーネルでプロセスが動かないのは、起動後すぐにSIGVを食らっているかららしい。原因を追いかけると、
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
                              unsigned long address)
{
:略
        vma = find_vma(mm, address);
        ↑ここで、linux-2.4だとvmaが返るが、linux-2.6だとNULLが返ることがある。(address=0x29555555の時)
        if (!vma)
                goto bad_area;
        if (vma->vm_start <= address)
                goto good_area;
                ↑linux-2.4だとここに行く。okだ。
        if (!(vma->vm_flags & VM_GROWSDOWN))
                goto bad_area;
        if (expand_stack(vma, address))
            ↑linux-2.6だとここにいってしまう。
                goto bad_area;
                ↑ここでSIGVる。/sbin/initが死んでおしまい。
gotoを使ってるのか〜なんて言わないでください。linuxカーネルはgoto大好きです。俺も最初は抵抗があったけど同一関数内でのgotoラベルジャンプは下手にフラグを使って見にくいコードになるよりよっぽどマシで、上のように読みやすい(理解しやすい)コードになる。good_ereaとかbad_ereaとか直読できる。

さて、linux-2.6ではdo_page_fault()の時、find_vma()に失敗するときがあるみたい。find_vma()は『プロセスのメモリリージョン一覧の中から指定されたアドレスに一番近いメモリリージョン(vma_erea_struct)を探す』関数だ。これがNULLを返す...うーむ。do_page_fault()でこのエラーってことはまずいな。直感ではここにはバグはなく、struct_mm の初期値に問題があるような気がする。(sh4では動いているみたいだし)

簡略化するためにメモリディスクリプタ(mm_struct)をmm、メモリリージョン(vm_erea_struct)をvmと略す。ひとつのプロセスはひとつのmmを持ち、mmには複数のvmが含まれる。mmからvmはlistとrb_tree(Red/Back/Tree 2色木)で探し出せる。find_vma()はmmを引数にとり、addrに一番近いvmを探す。vm内にaddrが含まれないときは、vm->vm_endがaddrよりも大きい最初のメンバを帰す。linux-2.4で上手くいくときのデータ構造は以下の通りだった。

find_vma(mm=0x8df11090, addr=0x29556000)
ここでmmに含まれるvmは4つ
 
*8DF0F130
vm_start = 0x400000
vm_end   = 0x457000
 
*8DF0F180
vm_start = 0x466000
vm_end   = 0x468000
 
*8DF0F1D0
vm_start = 0x468000
vm_end   = 0x469000
 
*8DF0F220
vm_start = 0x29556000
vm_end   = 0x29557000
『vm->vm_endがaddrよりも大きい最初のメンバ』という条件から検索に成功し8DF0F220をリターンする。vmあるじゃん。問題ない。このvmがいつ作られたかを追いかける
mm/mmapc.c:
do_mmap_pgoff(addr=0x29556000, len=0x431370)
  get_unmapped_area(0x29556000)
    arch_get_unmapped_area(0x29556000)
  munmap_back:
  find_vma_prepare(mm,addr=0x29556000,*prev) prev =>
    新しいvmを捻じ込むスペースを探す?
  vm_enouch_memory(pages=1){
    free = page_cache_size;   => 0x78
    free += nf_free_pages();  => 0x1c9f
    free += nr_swap_pages;    => 0
    free += swapper_space.nrpages; => 0
    free => 0x1d17
  }