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

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

vfree/vunmap(非連続メモリの解放)


vmalloc/vmap関数でアロケートしたメモリはvfree/vunmap関数で返されます。両者とも__vunmap関数で実際の処理が行われます。違いはvfreeから呼ばれた__vunmapでは、ページその物も返しますが、vunmapからではページそのものは解放しません。vmap関数でのメモリ取得は、ページそのものはvmap関数を呼だす側で用意しておく必要があり、メモリー解放において__vunmap関数でページは解放させないというのは処理の流れで頷けます。

__vunmap関数はページ単位にメモリー解放で、その点スラブ単位のメモリー解放のkfree関数と比べたら簡単です。まず解放するアドレスの正当性をチェックします。if ((PAGE_SIZE-1) & (unsigned long)addr)はアドレスがページアライメントかどうかのチェックをしています。

remove_vm_area関数でカーネルのリニア空間を管理しているvmlistから、そのメモリのメモリーリージョンを削除します。

deallocate_pagesが0でないなら、struct vm_structの情報をもとに、そのページを__free_pageでバディーシステムに戻します。すべてのページを戻した後そのページ情報を管理しているarea->pagesの領域を開放します。なお、その領域がvmallocで取得したか、kmallocで取得したかで、vfreeないしkfree関数を呼び出します。

そして最後にそのメモリリージョンを管理しているstruct vm_structを解放して終了です。
static void __vunmap(const void *addr, int deallocate_pages)
{
       struct vm_struct *area;

       if (!addr)
               return;

       if ((PAGE_SIZE-1) & (unsigned long)addr) {
               WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
               return;
       }

       area = remove_vm_area(addr);
       if (unlikely(!area)) {
               WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
                               addr);
               return;
       }

       debug_check_no_locks_freed(addr, area->size);
       debug_check_no_obj_freed(addr, area->size);

       if (deallocate_pages) {
               int i;

               for (i = 0; i < area->nr_pages; i++) {
                       struct page *page = area->pages[i];

                       BUG_ON(!page);
                       __free_page(page);
               }

               if (area->flags & VM_VPAGES)
                       vfree(area->pages);
               else
                       kfree(area->pages);
       }

       kfree(area);
       return;
}
カーネルのリニア空間(vm_struct)の管理はvmlistをヘッドとして、vmlist->nextを次のメモリ空間とする単方向リストで管理され、最後はvmlist->next=NULLとなっています。従ってif (tmp->addr == addr)なるメモリ空間をvmlistのリストを辿ることで見つけます。

見つかると、そのメモリ空間をunmap_vm_area関数で削除し、かかるページテーブルをクリアします。*p = tmp->nextについては、たぶん*pがtmpの前のメモリー空間で、nextで、そこに削除するtmp->nextを次のメモリ空間としてセットしているのでは・・・
static struct vm_struct *__remove_vm_area(const void *addr)
{
       struct vm_struct **p, *tmp;

       for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {
                if (tmp->addr == addr)
                        goto found;
       }
       return NULL;

found:
       unmap_vm_area(tmp);
       *p = tmp->next;

       tmp->size -= PAGE_SIZE;
       return tmp;
}
unmap_vm_area関数では削除するメモリー空間が有しているページテーブルを、pgd_offset_kマクロでマスタカーネルページグローバルディレクトリを取得し、順にページアッパディレクトリ,ページミドルディレクトリ、ページテーブルエントリーとそのエントリをクリアしています。
static void unmap_vm_area(struct vm_struct *area)
{
       unmap_kernel_range((unsigned long)area->addr, area->size);
}

void unmap_kernel_range(unsigned long addr, unsigned long size)
{
       pgd_t *pgd;
       unsigned long next;
       unsigned long start = addr;
       unsigned long end = addr + size;

       BUG_ON(addr >= end);
       pgd = pgd_offset_k(addr);
       flush_cache_vunmap(addr, end);
       do {
               next = pgd_addr_end(addr, end);
               if (pgd_none_or_clear_bad(pgd))
                       continue;
               vunmap_pud_range(pgd, addr, next);
       } while (pgd++, addr = next, addr != end);
       flush_tlb_kernel_range(start, end);
}
補足
ページテーブルのクリアはカーネルマスタディレクトリに施しています。そうなるとカーネルモードで動作しているプロセスがメモリを解放しても、そのプロセスのページテーブルは、解放されたページがまだ設定されているということになるのでは・・・。そうなるそのプロセスでは解放したエリアでも参照できるのでは? たぶんカーネルということで、その辺りは踏まえてvmalloc/vfrreeを使用しなさい。ということなのでしょうか?

最終更新 2011/02/20 17:26:48 - north
(2011/02/20 17:26:48 作成)