/proc/pid/maps のフラグ rwxp における p とは

マッピング領域がプライベートマッピングされていることを表す.
プライベートファイルマッピングでは,マッピング領域への変更はマッピング対象ファイルに影響を与えない.例えば,gnome-terminal の maps を見ると,

$ cat /proc/`pgrep gnome-terminal | head -n 1`/maps
08045000-0808d000 r-xp 00000000 fd:00 131656     /usr/bin/gnome-terminal
0808d000-0808f000 rw-p 00048000 fd:00 131656     /usr/bin/gnome-terminal
...

となっており,元のファイルの text/data セクションはプライベートマッピングされていることが分かる.
これはコピーオンライトを用いて実装されており,マッピング領域の変更時にページフォルトが発生し,その際( mm/memory.c do_wp_page()) にて


匿名マッピング用のページが確保され new_page = alloc_page_vma(GFP_HIGHUSER, vma, address);
変更対象がコピーされ cow_user_page(new_page, old_page, address, vma);
vma 中の変更前のページが取り除かれ page_remove_rmap(old_page, vma);
新しいページが追加される page_add_new_anon_rmap(new_page, vma, address);

sizeof と kobjsize

上記に関連して.
uClinuxで使う mm/nommu.c 中の重要関数 do_mmap_pgoff() では,

...
struct vm_area_struct *vma = NULL;
...
vma = kzalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
...
realalloc += kobjsize(vma);
askedalloc += sizeof(*vma);

としている箇所がある.
ここで,realalloc は実際に確保された値.askedalloc は本来欲しかった値.
sizeof演算子では,struct vm_area_struct のサイズが取得できる.
一方,kobjsize(vma) では,vma が kzalloc()(kmalloc()) によってスラブオブジェクトとして確保されるため,サイズは2の冪乗となり,その値が返る.
よって,realalloc には実際に確保された2の冪乗サイズ.askedalloc には要求された構造体のサイズが入る.
ちなみん,これら二つの変数は,2.6.23-mm1 現在全く使われていない.

スラブオブジェクトからサイズを取得するには(メモ)

kobjsize()

スラブオブジェクトのアドレス objp から,オブジェクトサイズを取得する.

kobjsize(スラブオブジェクトのアドレスobjp) 
↓
ksize(objp)
↓
return obj_size(virt_to_cache(objp))

virt_to_cache()

スラブオブジェクトのアドレス objp から キャッシュディスクリプタ kmem_cache を取得する.
kmem_cache は slab.h で定義され,slab_size,buffer_size メンバを持つ.obj_size() は buffer_size を返す.
この二つの違いは後で押さえる.

virt_to_cache(objp)
↓
struct page *page = virt_to_head_page(objp)
↓
return page_get_cache(page)

virt_to_head_page()

virt_to_head_page(objp)
↓
struct page *page = virt_to_page(objp)
↓
return compound_head(page);

virt_to_page() 重要

アドレスから,オブジェクトが属するページのページディスクリプタを取得する.
ページディスクリプタの取得には,ページの物理アドレスが必要.ここでオブジェクトのアドレスは仮想アドレスである.
x86でRAMのZONE_NORMAL(16MBから896MB)に対応する物理アドレスマッピングされた仮想アドレスの場合,リニアマッピングされているので,仮想アドレス-0xc0000000 で物理アドレスが取得できる.

virt_to_page(objp)
↓
pfn_to_page(__pa(objp) >> PAGE_SHIFT) 
→ARMの場合 #define __pa(x) __virt_to_phys((unsigned long)(x))
→x86の場合 #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)

__virt_to_phys() 重要

仮想アドレスから物理アドレスを得る.
ARMの場合,RAMが物理アドレスの0番地から始まるのではないため,RAMが開始するPHYS_OFFSETを物理アドレスに加算している.また,仮想アドレスはPAGE_OFFSET(大体の場合,3GB)以降にリニアマッピングされるため,それを減算している.こうやって仮想アドレスから物理アドレスを得る.

#define __virt_to_phys(x)       ((x) - PAGE_OFFSET + PHYS_OFFSET)

pfn_to_page() 重要

pfn=page frame number.ページの物理アドレスが分かると,アドレスをPAGE_SFHITすることでページフレーム番号も分かる.(CONFIG_FLATMEMの場合)ページディスクリプタはページディスクリプタ配列の先頭である mem_map と ページフレーム番号から取得できる.

#define __pfn_to_page(pfn)      (mem_map + ((pfn) - ARCH_PFN_OFFSET))

page_get_cache()

ページディスクリプタから,そのページの使用目的を表すキャッシュディスクリプタを得る.キャッシュ生成時に,page->lru.nextにセットされる(page_set_cache()).

return (struct kmem_cache *)page->lru.next;

メモリマッピングのデマンドページング - 2.6.23-mm1

(少なくとも2.6.22では) デマンドページング時は handle_pte_fault() から mm/memory.c do_no_page() が呼ばれていたが,2.6.23-mm1 では,do_no_page() は do_linear_fault() と関数名が変更されている.また,do_file_page() はdo_nolinear_fault() となり,do_file_page() と do_no_page() の中身が __do_fault() にまとめられ,do_linear_fault() と do_nolinear_fault() から呼ばれるようになっている.
.nopageメソッドも.faultメソッドに名前が変更してる.

Zim で TrackLink 的リンクを使用する方法

Zim で作業記録を書いていると,プロジェクト管理に使用している Trac へのリンクを使用する場面がしばしばあります.そこで Zim で TracLink 的リンクを使う方法を調べてみました.
/usr/share/zim/doc/zim/usage/linking.txt,/usr/share/zim/urls.list を参考にしました.
~/local/share/zim/urls.list に,

ticket  https:///ticket/

と書くと,Zim 中での

ticket?117

が,

https:///ticket/117

として変換されます.