2011年01月25日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2010/2011-01-25
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
卒業予定の4年生に対する特別措置として、2月7日(月曜日)6時間目 (16:45-18:00)に試験をすることを検討しています。 対象者は、授業終了後、前に集まって下さい。

図? プロセスのアドレス空間の構造
線形なアドレス空間は、メモリ・エリア(memory area)(または、memory resion、memory interval)に分割される。
include/linux/sched.h
1163: struct task_struct {
...
1219: struct mm_struct *mm, *active_mm;
...
1497: };
tast_struct の mm フィールド

図? プロセス関連のメモリの構造体
include/linux/mm_types.h
222: struct mm_struct {
223: struct vm_area_struct * mmap; /* list of VMAs */
224: struct rb_root mm_rb;
225: struct vm_area_struct * mmap_cache; /* last find_vma result */
...
236: pgd_t * pgd;
237: atomic_t mm_users; /* How many users with user space? */
238: atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
239: int map_count; /* number of VMAs */
...
243: struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
244: * together off init_mm.mmlist, and are protected
245: * by mmlist_lock
246: */
...
254: unsigned long start_code, end_code, start_data, end_data;
255: unsigned long start_brk, brk, start_stack;
256: unsigned long arg_start, arg_end, env_start, env_end;
...
289: };
130: struct vm_area_struct {
131: struct mm_struct * vm_mm; /* The address space we belong to. */
132: unsigned long vm_start; /* Our start address within vm_mm. */
133: unsigned long vm_end; /* The first byte after our end address
...
136: /* linked list of VM areas per task, sorted by address */
137: struct vm_area_struct *vm_next, *vm_prev;
...
139: pgprot_t vm_page_prot; /* Access permissions of this VMA. */
140: unsigned long vm_flags; /* Flags, see mm.h. */
141:
142: struct rb_node vm_rb;
...
171: const struct vm_operations_struct *vm_ops;
...
174: unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
175: units, *not* PAGE_CACHE_SIZE */
176: struct file * vm_file; /* File we map to (can be NULL). */
177: void * vm_private_data; /* was vm_pte (shared mem) */
...
186: };
| フラグ | 説明 |
|---|---|
| VM_READ | 読み込み可 |
| VM_WRITE | 書き込み可 |
| VM_EXEC | 実行可 |
| VM_SHARED | 共有されている |
| VM_GROWSDOWN | アドレスが小さい方に伸びる |
| VM_GROWSUP | アドレスが大きい方に伸びる |
| VM_DENYWRITE | 書き込み不可。 |
| VM_EXECUTABLE | 実行可能。 |
| VM_LOCKED | ロックされている。 |
| VM_DONTCOPY | コピー不可 |
| VM_DONTEXPAND | 拡張不可。 |

図? プロセスのアドレス空間の実現
/proc/PID/maps というファイルを見ると、その様子が分かる。
$ echo $$
3981
$ ls /proc/$$
attr cpuset fd maps oom_adj smaps task
auxv cwd io mem oom_score stat wchan
cmdline environ limits mounts root statm
coredump_filter exe loginuid mountstats schedstat status
$ cat /proc/$$/maps
00110000-00114000 r-xp 00000000 08:02 490576 /lib/libnss_dns-2.5.so
00114000-00115000 r--p 00003000 08:02 490576 /lib/libnss_dns-2.5.so
00115000-00116000 rw-p 00004000 08:02 490576 /lib/libnss_dns-2.5.so
...
08047000-080f5000 r-xp 00000000 08:02 481554 /bin/bash
080f5000-080fa000 rw-p 000ae000 08:02 481554 /bin/bash
080fa000-080ff000 rw-p 080fa000 00:00 0
09d66000-09e25000 rw-p 09d66000 00:00 0 [heap]
...
bffdd000-bfff2000 rw-p bffe9000 00:00 0 [stack]
$ wc /proc/$$/maps
45 263 2920 /proc/3981/maps
$
/proc/PID/maps のフィールドの意味
/proc/PID/maps では、人間にとって
分かりやすいようにわざわざ表示している。
ブロック・デバイスのメジャー番号とマイナー番号は、ls -l でわかる。
$ ls -l /dev/sda2
brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$
ファイルの inode 番号は、ls -i でわかる。
$ ls -li /bin/bash
481554 -rwxr-xr-x 1 root root 735004 Jan 22 2009 /bin/bash
$ ls -li /lib/libnss_dns-2.5.so
490576 -rwxr-xr-x 1 root root 21948 Oct 26 08:16 /lib/libnss_dns-2.5.so
$

図? MMUによる仮想アドレスから物理アドレスへの変換

図? 1段のページテーブル
ページテーブルは、次のような配列になる。
unsigned int page_table[0x100000];
unsigned long int physical_address( unsigned long int virtual v ) {
unsigned long int p, page, offset;
p = v >> 12; // 32中、上位20ビット(32-12==20)の取り出し
offset = v & 0xfff; // 下位 12 ビットの取り出し
page = page_table[p]
return( page + offset );
}

図? 1段のページテーブル
page_table[] は、0x100000 個 == 1024 * 1024 個 == 1M 個の要素からなる。 1要素が 4 バイト(32ビット) なら、4MB のメモリが必要になる。
仮想アドレスの構成の 例。 1ページが4KB、仮想アドレスが32ビットの時の分割の例(他の分割方法も考えら れる)

図? 仮想アドレスの4つの部分への分割例

図? 4段のページテーブル
unsigned int pgd[0x20];
unsigned long int physical_address( unsigned long int virtual v ) {
unsigned int *pud, *pmd, *pte, p, q, r, s, page, offset;
p = v >> (32-5) ;
q = (v >> (32-10)) & 0x1f;
r = (v >> (32-15)) & 0x1f;
s = (v >> (32-20)) & 0x1f;
offset = v & 0xfff;
pud = pgd[p];
pmd = pud[q];
pte = pmd[r];
page = pte[s]
return( page + offset );
}

図? 仮想アドレスの3つの部分への分割例

図? x86の2段のページテーブル
arch/x86/mm/fault.c
948: dotraplinkage void __kprobes
949: do_page_fault(struct pt_regs *regs, unsigned long error_code)
950: {
951: struct vm_area_struct *vma;
952: struct task_struct *tsk;
953: unsigned long address;
954: struct mm_struct *mm;
955: int write;
956: int fault;
957:
958: tsk = current;
959: mm = tsk->mm;
...
962: address = read_cr2();
...
1077: vma = find_vma(mm, address);
1078: if (unlikely(!vma)) {
1079: bad_area(regs, error_code, address);
1080: return;
1081: }
1082: if (likely(vma->vm_start <= address))
1083: goto good_area;
1084: if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
1085: bad_area(regs, error_code, address);
1086: return;
1087: }
...
1100: if (unlikely(expand_stack(vma, address))) {
1101: bad_area(regs, error_code, address);
1102: return;
1103: }
...
1109: good_area:
1110: write = error_code & PF_WRITE;
1111:
1112: if (unlikely(access_error(error_code, write, vma))) {
1113: bad_area_access_error(regs, error_code, address);
1114: return;
1115: }
..
1122: fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
...
1142: }
mm/memory.c
3198: int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
3199: unsigned long address, unsigned int flags)
3200: {
3201: pgd_t *pgd;
3202: pud_t *pud;
3203: pmd_t *pmd;
3204: pte_t *pte;
...
3216: pgd = pgd_offset(mm, address);
3217: pud = pud_alloc(mm, pgd, address);
3218: if (!pud)
3219: return VM_FAULT_OOM;
3220: pmd = pmd_alloc(mm, pud, address);
3221: if (!pmd)
3222: return VM_FAULT_OOM;
3223: pte = pte_alloc_map(mm, pmd, address);
3224: if (!pte)
3225: return VM_FAULT_OOM;
3226:
3227: return handle_pte_fault(mm, vma, address, pte, pmd, flags);
3228: }
3142: static inline int handle_pte_fault(struct mm_struct *mm,
3143: struct vm_area_struct *vma, unsigned long address,
3144: pte_t *pte, pmd_t *pmd, unsigned int flags)
3145: {
3146: pte_t entry;
...
3149: entry = *pte;
...
3151: if (pte_none(entry)) {
3152: if (vma->vm_ops) {
3153: if (likely(vma->vm_ops->fault))
3154: return do_linear_fault(mm, vma, address,
3155: pte, pmd, flags, entry);
3156: }
3157: return do_anonymous_page(mm, vma, address,
3158: pte, pmd, flags);
3159: }
3160: if (pte_file(entry))
3161: return do_nonlinear_fault(mm, vma, address,
3162: pte, pmd, flags, entry);
3163: return do_swap_page(mm, vma, address,
3164: pte, pmd, flags, entry);
...
3193: }
vma->vm_ops->fault という関数があれば、
do_linear_fault() で処理する。
0x00000000 から 0x00000fff まで
0x00001000 から 0x00001fff まで
0xfffff000 から 0xffffffff まで