2024年01月17日
情報科学類 オペレーティングシステム II
                                       筑波大学 システム情報系 
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	https://www.coins.tsukuba.ac.jp/~yas/coins/os2-2023/2024-01-17
あるいは、次のページから手繰っていくこともできます。
	https://www.coins.tsukuba.ac.jp/~yas/
	https://www.cs.tsukuba.ac.jp/~yas/
![struct page page[]、物理メモリ](images/struct-page-page.png)
図? struct page page[]による物理メモリの管理
linux-6.6.9/include/linux/mm_types.h
  74:	struct page {
  75:	        unsigned long flags;            /* Atomic flags, some possibly
  76:	                                         * updated asynchronously */
...
  91:	                                struct list_head lru;
...
 106:	                        struct address_space *mapping;
...
 181:	        atomic_t _refcount;
...
 198:	        void *virtual;                  /* Kernel virtual address (NULL if
 199:	                                           not kmapped, ie. highmem) */
...
 217:	} _struct_page_alignment;
| PG_locked | ページがピン留めされている。ページアウトされない。入出力の処理中に設定され、完了後に解除される。 | 
| PG_referenced | ディスク入出力のために参照されている。 | 
| PG_uptodate | ページの内容が有効である。入力処理が完了した。 | 
| PG_dirty | ページの内容が変更された。 | 
| PG_lru | ページングのための LRU リストにある。 | 
| PG_active | ページがアクティブである。 | 
| PG_error | このページに対して入出力エラーが生じた。 | 
| PG_slab | スラブ・アロケータで割り当てられた。 | 
| PG_arch_1 | アーキテクチャ固有のページ状態 | 
| PG_reserved | ページアウト禁止、または、ブード時のメモリ・アロケータで割り当てられた | 
| PG_writeback | 書き戻し中 | 
| PG_reclaim | 開放すべきページ | 
linux-6.6.9/arch/x86/include/asm/page_types.h 10: #define PAGE_SHIFT 12 11: #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) 12: #define PAGE_MASK (~(PAGE_SIZE-1)) linux-6.6.9/include/uapi/linux/const.h 20: #define __AC(X,Y) (X##Y) 21: #define _AC(X,Y) __AC(X,Y)
よく使われるゾーンの種類。
図? メモリのゾーンへの分割(x86-32)
図? メモリのゾーンへの分割(x86-64)
DMA は、ハードディスクやネットワークデバイス等で使われているの入出力方 法の1つ。通常、メモリは、CPU が制御している。DMA では、周辺デバイスが CPU からメモリの制御を奪い、データの入出力を行う。
DMA の利点
次のような手続きで、メモリを開放する。
linux-6.6.9/include/linux/gfp.h 280: #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) 177: struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid, 178: nodemask_t *nodemask); 289: extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); 290: extern unsigned long get_zeroed_page(gfp_t gfp_mask); 302: extern void __free_pages(struct page *page, unsigned int order); 303: extern void free_pages(unsigned long addr, unsigned int order); 320: #define free_page(addr) free_pages((addr), 0)
__get_free_pages(), alloc_pages(), alloc_page() や、 後述する kmalloc() では、 gfp_t のフラグ(gfp_mask) として、次のものがよく使われる。特に GFP_KERNEL がよく使われる。gfp は、get free pages に由来する。
linux-6.6.9/include/linux/gfp_types.h
| 型 | 説明 | 
|---|---|
| GFP_ATOMIC | 高優先度。スリープ不可。割込み処理の前半(割り込みハンドラ、top half)や後半(bottom half)で使う。 | 
| GFP_KERNEL | カーネル用メモリ通常の方法。スリープ可。ユーザ・プロセスのコンテキストで使う。 | 
| GFP_NOIO | スリープ可、入出力不可。 | 
| GFP_NOFS | スリープ化、入出力可、ファイル操作不可。ファイルシステムの実装で使う(他のファイルシステムの操作を開始しない)。 | 
| GFP_USER | ユーザ空間用のメモリの通常の方法。スリープ可。 | 
| GFP_HIGHUSER | HIGHMEMゾーンからの割当て。スリープ可。 | 
| GFP_DMA | DMAゾーンからの割当て。デバイス・ドライバ等が使う。 | 
| GFP_DMA32 | DMA32ゾーンからの割当て。デバイス・ドライバ等が使う。 | 
buddy とは、仲間の意味。
| 0 | 1 | 
| 2 | 3 | 
| 4 | 5 | 
| 6 | 7 | 
| 0,1 | 2,3 | 
| 4,5 | 6,7 | 
| 0,1,2,3 | 4,5,6,7 | 
Buddyシステムでのメモリ管理手法

図1(a) Buddyシステムによる空きページの管理(論理的な見方)

図1(b) Buddyシステムによる空きページの管理(線形な見方)
$ cat /proc/buddyinfo ![[←]](../icons/screen-return.gif) Node 0, zone      DMA      1      1      1      1      1      1      1      0      1      1      3
Node 0, zone    DMA32   3544   7608   4945   3129   1783    840    337    121     33      0      0
Node 0, zone   Normal  27583   9215  14233   4512   1288    208    127     20      3      1      0
$
Node 0, zone      DMA      1      1      1      1      1      1      1      0      1      1      3
Node 0, zone    DMA32   3544   7608   4945   3129   1783    840    337    121     33      0      0
Node 0, zone   Normal  27583   9215  14233   4512   1288    208    127     20      3      1      0
$ ![[]](../icons/screen-cursor.gif) 
この例では、DMA ゾーンの 2 0 (4KB) に、1 個、2 1 (8KB) に、1 個、・・・、2 10 に3個の空きがある。
外部フラグメンテーションが起きると、大きな塊が少なくなる。
x86 (32ビット)の例
% cat /proc/buddyinfo ![[←]](../icons/screen-return.gif) Node 0, zone      DMA      6      5      3      3      4      2      2      0      1      1      2 
Node 0, zone   Normal    476   2577    990    354    174    104     65     34     19      1    135 
Node 0, zone  HighMem   1416   2920   1718   1082    933    504    251    152     87     43     53 
%
Node 0, zone      DMA      6      5      3      3      4      2      2      0      1      1      2 
Node 0, zone   Normal    476   2577    990    354    174    104     65     34     19      1    135 
Node 0, zone  HighMem   1416   2920   1718   1082    933    504    251    152     87     43     53 
% ![[]](../icons/screen-cursor.gif) 
この例では、DMA ゾーンの 2 0 (4KB) に、6 個、
2 1 (8KB) に、5 個、・・・、2 10 に2個の空きがある。
外部フラグメンテーションが起きると、大きな塊が少なくなる。
void *kmalloc(size_t size, gfp_t flags)引数
| 状況 | フラグ | 
| プロセスのコンテキスト、スリープ可能 | GFP_KERNEL | 
| プロセスのコンテキスト、スリープ不可 | GFP_ATOMIC | 
| 割込みハンドラ | GFP_ATOMIC | 
| 割込みハンドラ後半(Softirq,Tasklet,後述) | GFP_ATOMIC | 
| DMA可能なメモリ、スリープ可能 | GFP_DMA|GFP_KERNEL | 
| DMA可能なメモリ、スリープ不可 | GFP_DMA|GFP_ATOMIC | 
void kfree(const void *objp)C言語のユーザ空間で使えるライブラリ free() と似ている。 kmalloc() で割り当てたメモリを解放する。

図? フリーリストの例

図? フリーリストの例(ページを意識)

図? ページ・フレーム、スラブ、オブジェクトの関係
struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
        unsigned long flags, void (*ctor)(void *))
引数
void kmem_cache_destroy(struct kmem_cache *c)kmem_cache_create() で割り当てた struct kmem_cache *を開放する。 shutdown (電源を切る操作)で呼ばれることがある。
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) void *kmem_cache_alloc_node(struct kmem_cache *cachep,gfp_t flags, int node) void kmem_cache_free(struct kmem_cache *cachep, void *b)生成した struct kmem_cache *を使ってオブジェクトのメモリを割り当てる。 割り当てたオブジェクトのメモリは、kmem_cache_free()で開放する。
kmem_cache_alloc_node() は、メモリ・アクセスが不均質なマルチプロセッサ 用。メモリを割り当てるという働きは、kmem_cache_alloc() と同じ。ただし、 node で指定されたプロセッサで高速にアクセスできるメモリに割り当てられる。
linux-6.6.9/kernel/cred.c
 603:	void __init cred_init(void)
 604:	{
 605:	        /* allocate a slab in which we can store credentials */
 606:	        cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), 0,
 607:	                        SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL);
 608:	}
 201:	struct cred *prepare_creds(void)
 202:	{
 203:	        struct task_struct *task = current;
 204:	        const struct cred *old;
 205:	        struct cred *new;
 206:	
 207:	        new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
...
 213:	        old = task->cred;
 214:	        memcpy(new, old, sizeof(struct cred));
...
 240:	        return new;
...
 245:	}
  68:	static void put_cred_rcu(struct rcu_head *rcu)
  69:	{
  70:	        struct cred *cred = container_of(rcu, struct cred, rcu);
...
  89:	        kmem_cache_free(cred_jar, cred);
  90:	}
# cat /proc/slabinfo ![[←]](../icons/screen-return.gif) slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
nfs_direct_cache       0      0    352   23    2 : tunables    0    0    0 : slabdata      0      0      0
nfs_commit_data       92     92    704   23    4 : tunables    0    0    0 : slabdata      4      4      0
...
UDP                  240    240   1088   30    8 : tunables    0    0    0 : slabdata      8      8      0
tw_sock_TCP          160    160    256   16    1 : tunables    0    0    0 : slabdata     10     10      0
TCP                  155    240   1984   16    8 : tunables    0    0    0 : slabdata     15     15      0
...
task_struct          763    784   4224    7    8 : tunables    0    0    0 : slabdata    112    112      0
cred_jar            1588   2982    192   21    1 : tunables    0    0    0 : slabdata    142    142      0
anon_vma           18794  19584     80   51    1 : tunables    0    0    0 : slabdata    384    384      0
pid                 1984   1984    128   32    1 : tunables    0    0    0 : slabdata     62     62      0
...
dma-kmalloc-8192       0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4096       0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
...
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
...
kmalloc-8192          74     88   8192    4    8 : tunables    0    0    0 : slabdata     22     22      0
kmalloc-4096         371    392   4096    8    8 : tunables    0    0    0 : slabdata     49     49      0
...
kmalloc-32         47009  48256     32  128    1 : tunables    0    0    0 : slabdata    377    377      0
kmalloc-16        115733 117760     16  256    1 : tunables    0    0    0 : slabdata    460    460      0
kmalloc-8          65406  68608      8  512    1 : tunables    0    0    0 : slabdata    134    134      0
kmem_cache_node      320    320     64   64    1 : tunables    0    0    0 : slabdata      5      5      0
kmem_cache           160    160    256   16    1 : tunables    0    0    0 : slabdata     10     10      0
#
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
nfs_direct_cache       0      0    352   23    2 : tunables    0    0    0 : slabdata      0      0      0
nfs_commit_data       92     92    704   23    4 : tunables    0    0    0 : slabdata      4      4      0
...
UDP                  240    240   1088   30    8 : tunables    0    0    0 : slabdata      8      8      0
tw_sock_TCP          160    160    256   16    1 : tunables    0    0    0 : slabdata     10     10      0
TCP                  155    240   1984   16    8 : tunables    0    0    0 : slabdata     15     15      0
...
task_struct          763    784   4224    7    8 : tunables    0    0    0 : slabdata    112    112      0
cred_jar            1588   2982    192   21    1 : tunables    0    0    0 : slabdata    142    142      0
anon_vma           18794  19584     80   51    1 : tunables    0    0    0 : slabdata    384    384      0
pid                 1984   1984    128   32    1 : tunables    0    0    0 : slabdata     62     62      0
...
dma-kmalloc-8192       0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4096       0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
...
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
...
kmalloc-8192          74     88   8192    4    8 : tunables    0    0    0 : slabdata     22     22      0
kmalloc-4096         371    392   4096    8    8 : tunables    0    0    0 : slabdata     49     49      0
...
kmalloc-32         47009  48256     32  128    1 : tunables    0    0    0 : slabdata    377    377      0
kmalloc-16        115733 117760     16  256    1 : tunables    0    0    0 : slabdata    460    460      0
kmalloc-8          65406  68608      8  512    1 : tunables    0    0    0 : slabdata    134    134      0
kmem_cache_node      320    320     64   64    1 : tunables    0    0    0 : slabdata      5      5      0
kmem_cache           160    160    256   16    1 : tunables    0    0    0 : slabdata     10     10      0
# ![[]](../icons/screen-cursor.gif) 
スラブ・アロケータには、2種類ある。
size-番号 。
DMA が付いているものは、DMA 可能なメモリ。
図? プロセスのアドレス空間の構造(64ビット)
0x00000000-0xffffffff のうち0xc0000000-0xffffffff の間は、
オペレーティング・システム・カーネルが使うので、ユーザ空間では使えない。
ELF ファイルは、ヘッダとセクションの並びからなる。重要なセクションに は、.text, .rodata, .data がある。
$ file hello.c ![[←]](../icons/screen-return.gif) hello.c: C source, ASCII text
$ cat hello.c
hello.c: C source, ASCII text
$ cat hello.c ![[←]](../icons/screen-return.gif) #include <stdio.h>
int main()
{
        printf("hello, %s!\n","world");
}
$ cc -o hello hello.c
#include <stdio.h>
int main()
{
        printf("hello, %s!\n","world");
}
$ cc -o hello hello.c ![[←]](../icons/screen-return.gif) $ file hello
$ file hello ![[←]](../icons/screen-return.gif) hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
 (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=68c4fff13c07df8738b2e
0f20a27f9a03a20f5c1, not stripped
$ size hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
 (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=68c4fff13c07df8738b2e
0f20a27f9a03a20f5c1, not stripped
$ size hello ![[←]](../icons/screen-return.gif) text	   data	    bss	    dec	    hex	filename
   1234    548        4    1786     6fa	hello
$ readelf -S hello
   text	   data	    bss	    dec	    hex	filename
   1234    548        4    1786     6fa	hello
$ readelf -S hello ![[←]](../icons/screen-return.gif) There are 30 section headers, starting at offset 0x1928:
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
...
  [13] .text             PROGBITS         0000000000400440  00000440
       0000000000000182  0000000000000000  AX       0     0     16
...
  [15] .rodata           PROGBITS         00000000004005d0  000005d0
       0000000000000022  0000000000000000   A       0     0     8
...
  [24] .data             PROGBITS         0000000000601030  00001030
       0000000000000004  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           0000000000601034  00001034
       0000000000000004  0000000000000000  WA       0     0     1
...
  [27] .symtab           SYMTAB           0000000000000000  00001068
       00000000000005e8  0000000000000018          28    46     8
...
$
There are 30 section headers, starting at offset 0x1928:
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
...
  [13] .text             PROGBITS         0000000000400440  00000440
       0000000000000182  0000000000000000  AX       0     0     16
...
  [15] .rodata           PROGBITS         00000000004005d0  000005d0
       0000000000000022  0000000000000000   A       0     0     8
...
  [24] .data             PROGBITS         0000000000601030  00001030
       0000000000000004  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           0000000000601034  00001034
       0000000000000004  0000000000000000  WA       0     0     1
...
  [27] .symtab           SYMTAB           0000000000000000  00001068
       00000000000005e8  0000000000000018          28    46     8
...
$ ![[]](../icons/screen-cursor.gif) 
int execve(const char *filename, char *const argv[], char *const envp[])
線形なアドレス空間は、メモリ・エリア(memory area)(または、memory region、memory interval)に分割される。
linux-6.6.9/include/linux/sched.h
 743:	struct task_struct {
...
 876:	        struct mm_struct                *mm;
...
1554:	};
tast_struct の mm フィールド
linux-6.6.9/include/linux/mm_types.h
 673:	struct mm_struct {
...
 687:	                        atomic_t mm_count;
...
 690:	                struct maple_tree mm_mt;
...
 704:	                pgd_t * pgd;
...
 725:	                atomic_t mm_users;
...
 746:	                int map_count;                  /* number of VMAs */
...
 765:	                struct list_head mmlist;
...
 809:	                unsigned long start_code, end_code, start_data, end_data;
 810:	                unsigned long start_brk, brk, start_stack;
 811:	                unsigned long arg_start, arg_end, env_start, env_end;
...
 927:	};
linux-6.6.9/include/linux/mm_types.h
 565:	struct vm_area_struct {
...
 571:	                        unsigned long vm_start;
 572:	                        unsigned long vm_end;
...
 579:	        struct mm_struct *vm_mm;        /* The address space we belong to. */
 580:	        pgprot_t vm_page_prot;          /* Access permissions of this VMA. */
...
 587:	                const vm_flags_t vm_flags;
...
 634:	        const struct vm_operations_struct *vm_ops;
...
 639:	        struct file * vm_file;          /* File we map to (can be NULL). */
...
 663:	} __randomize_layout;
| フラグ | 説明 | 
|---|---|
| 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 $$ ![[←]](../icons/screen-return.gif) 22696
$ ls /proc/$$
22696
$ ls /proc/$$ ![[←]](../icons/screen-return.gif) attr		 cwd	   map_files   oom_adj	      schedstat  task
autogroup	 environ   maps        oom_score      sessionid  timers
auxv		 exe	   mem	       oom_score_adj  setgroups  uid_map
cgroup		 fd	   mountinfo   pagemap	      smaps	 wchan
clear_refs	 fdinfo    mounts      patch_state    stack
cmdline		 gid_map   mountstats  personality    stat
comm		 io	   net	       projid_map     statm
coredump_filter  limits    ns	       root	      status
cpuset		 loginuid  numa_maps   sched	      syscall
$ cat /proc/$$/maps
attr		 cwd	   map_files   oom_adj	      schedstat  task
autogroup	 environ   maps        oom_score      sessionid  timers
auxv		 exe	   mem	       oom_score_adj  setgroups  uid_map
cgroup		 fd	   mountinfo   pagemap	      smaps	 wchan
clear_refs	 fdinfo    mounts      patch_state    stack
cmdline		 gid_map   mountstats  personality    stat
comm		 io	   net	       projid_map     statm
coredump_filter  limits    ns	       root	      status
cpuset		 loginuid  numa_maps   sched	      syscall
$ cat /proc/$$/maps ![[←]](../icons/screen-return.gif) 00400000-004de000 r-xp 00000000 fd:00 403270428                          /usr/bin/bash
006dd000-006de000 r--p 000dd000 fd:00 403270428                          /usr/bin/bash
006de000-006e7000 rw-p 000de000 fd:00 403270428                          /usr/bin/bash
006e7000-006ed000 rw-p 00000000 00:00 0
00b7a000-00bdc000 rw-p 00000000 00:00 0                                  [heap]
...
7fb437f5e000-7fb438122000 r-xp 00000000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438122000-7fb438321000 ---p 001c4000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438321000-7fb438325000 r--p 001c3000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438325000-7fb438327000 rw-p 001c7000 fd:00 50035                      /usr/lib64/libc-2.17.so
7ffc24c64000-7ffc24c85000 rw-p 00000000 00:00 0                          [stack]
7ffc24dce000-7ffc24dd0000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ wc /proc/$$/maps
00400000-004de000 r-xp 00000000 fd:00 403270428                          /usr/bin/bash
006dd000-006de000 r--p 000dd000 fd:00 403270428                          /usr/bin/bash
006de000-006e7000 rw-p 000de000 fd:00 403270428                          /usr/bin/bash
006e7000-006ed000 rw-p 00000000 00:00 0
00b7a000-00bdc000 rw-p 00000000 00:00 0                                  [heap]
...
7fb437f5e000-7fb438122000 r-xp 00000000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438122000-7fb438321000 ---p 001c4000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438321000-7fb438325000 r--p 001c3000 fd:00 50035                      /usr/lib64/libc-2.17.so
7fb438325000-7fb438327000 rw-p 001c7000 fd:00 50035                      /usr/lib64/libc-2.17.so
7ffc24c64000-7ffc24c85000 rw-p 00000000 00:00 0                          [stack]
7ffc24dce000-7ffc24dd0000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ wc /proc/$$/maps ![[←]](../icons/screen-return.gif) 41 239 3630 /proc/22696/maps
$
41 239 3630 /proc/22696/maps
$ ![[]](../icons/screen-cursor.gif) 
/proc/PID/maps のフィールドの意味
/proc/PID/maps では、人間にとって
分かりやすいようにわざわざ表示している。
ブロック・デバイスのメジャー番号とマイナー番号は、ls -l でわかる。
$ ls -l /dev/sda2 ![[←]](../icons/screen-return.gif) brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$
brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$ ![[]](../icons/screen-cursor.gif) 
ファイルの inode 番号は、ls -i でわかる。
$ ls -li /bin/bash /usr/lib64/libc-2.17.so ![[←]](../icons/screen-return.gif) 403270428 -rwxr-xr-x 1 root root  964536 Oct 27  2021 /bin/bash
    50035 -rwxr-xr-x 1 root root 2156592 Mar 23  2023 /usr/lib64/libc-2.17.so
$
403270428 -rwxr-xr-x 1 root root  964536 Oct 27  2021 /bin/bash
    50035 -rwxr-xr-x 1 root root 2156592 Mar 23  2023 /usr/lib64/libc-2.17.so
$ ![[]](../icons/screen-cursor.gif) 

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

図? 1段のページテーブル
unsigned long int page_table[0x100000];この配列の要素は、ページ・フレームの先頭番地(物理アドレス)。
MMU(ハードウェア) は、このページテーブルを使って、次のようにして仮想ア ドレスから物理アドレスを求める。以下は、MMU の動きを C 言語で説明したも の。
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ビットの時の分割の例(他の分割方法も考えら れる)

図? 仮想アドレス上位20ビットの5つの部分への分割例
図? 5段のページテーブル
unsigned int pgd[0x10];
unsigned long int physical_address( unsigned long int virtual v ) {
    unsigned int *pud, *p4d, *pmd, *pte, p, q, r, s, t, page, offset;
    p = v >>  (32-4) ;
    q = (v >> (32-8)) & 0xf;
    r = (v >> (32-12)) & 0xf;
    s = (v >> (32-16)) & 0xf;
    t = (v >> (32-20)) & 0xf;
    offset = v & 0xfff;
    p4d = pgd[p];
    pud = p4d[q];
    pmd = pud[r];
    pte = pmd[s];
    page = pte[t]
    return( page + offset );
}

図? 仮想アドレス位20ビットの2つの部分への分割例
図? x86の2段のページテーブル
linux-6.6.9/arch/x86/include/asm/idtentry.h
 165:	#define DEFINE_IDTENTRY_RAW_ERRORCODE(func)                             \
 166:	__visible noinstr void func(struct pt_regs *regs, unsigned long error_code)
linux-6.6.9/arch/x86/mm/fault.c
1517:	DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault)
1518:	{
1519:	        unsigned long address = read_cr2();
...
1561:	        handle_page_fault(regs, error_code, address);
...
1565:	}
1492:	static __always_inline void
1493:	handle_page_fault(struct pt_regs *regs, unsigned long error_code,
1494:	                              unsigned long address)
1495:	{
...
1502:	        if (unlikely(fault_in_kernel_space(address))) {
1503:	                do_kern_addr_fault(regs, error_code, address);
1504:	        } else {
1505:	                do_user_addr_fault(regs, error_code, address);
...
1514:	        }
1515:	}
1238:	static inline
1239:	void do_user_addr_fault(struct pt_regs *regs,
1240:	                        unsigned long error_code,
1241:	                        unsigned long address)
1242:	{
1243:	        struct vm_area_struct *vma;
1244:	        struct task_struct *tsk;
1245:	        struct mm_struct *mm;
1246:	        vm_fault_t fault;
1247:	        unsigned int flags = FAULT_FLAG_DEFAULT;
1248:	
1249:	        tsk = current;
1250:	        mm = tsk->mm;
...
1356:	        vma = lock_vma_under_rcu(mm, address);
1357:	        if (!vma)
1358:	                goto lock_mmap;
...
1364:	        fault = handle_mm_fault(vma, address, flags | FAULT_FLAG_VMA_LOCK, regs);
...
1395:	        if (unlikely(access_error(error_code, vma))) {
1396:	                bad_area_access_error(regs, error_code, address, vma);
1397:	                return;
1398:	        }
...
1413:	        fault = handle_mm_fault(vma, address, flags, regs);
...
1382:	lock_mmap:
..
1385:	        vma = lock_mm_and_find_vma(mm, address, regs);
...
1413:	        fault = handle_mm_fault(vma, address, flags, regs);
...
1476:	}
linux-6.6.9/include/linux/mm.h
 508:	struct vm_fault {
...
 552:	};
linux-6.6.9/mm/memory.c
5255:	vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
5256:	                           unsigned int flags, struct pt_regs *regs)
5257:	{
5258:	        /* If the fault handler drops the mmap_lock, vma may be freed */
5259:	        struct mm_struct *mm = vma->vm_mm;
...
5268:	        if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
5269:	                                            flags & FAULT_FLAG_INSTRUCTION,
5270:	                                            flags & FAULT_FLAG_REMOTE)) {
5271:	                ret = VM_FAULT_SIGSEGV;
5272:	                goto out;
5273:	        }
5274:	
...
5287:	                ret = __handle_mm_fault(vma, address, flags);
...
5305:	        return ret;
5306:	}
5031:	static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
5032:	                unsigned long address, unsigned int flags)
5033:	{
5034:	        struct vm_fault vmf = {
5035:	                .vma = vma,
5036:	                .address = address & PAGE_MASK,
5037:	                .real_address = address,
5038:	                .flags = flags,
5039:	                .pgoff = linear_page_index(vma, address),
5040:	                .gfp_mask = __get_fault_gfp_mask(vma),
5041:	        };
5042:	        struct mm_struct *mm = vma->vm_mm;
5043:	        unsigned long vm_flags = vma->vm_flags;
5044:	        pgd_t *pgd;
5045:	        p4d_t *p4d;
5046:	        vm_fault_t ret;
5047:	
5048:	        pgd = pgd_offset(mm, address);
5049:	        p4d = p4d_alloc(mm, pgd, address);
...
5053:	        vmf.pud = pud_alloc(mm, p4d, address);
...
5083:	        vmf.pmd = pmd_alloc(mm, vmf.pud, address);
...
5122:	        return handle_pte_fault(&vmf);
5123:	}
linux-6.6.9/mm/memory.c
4947:	static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
4948:	{
...
4967:	                vmf->pte = pte_offset_map_nolock(vmf->vma->vm_mm, vmf->pmd,
4968:	                                                 vmf->address, &vmf->ptl);
...
4971:	                vmf->orig_pte = ptep_get_lockless(vmf->pte);
4972:	                vmf->flags |= FAULT_FLAG_ORIG_PTE_VALID;
...
4980:	        if (!vmf->pte)
4981:	                return do_pte_missing(vmf);
4982:	
4983:	        if (!pte_present(vmf->orig_pte))
4984:	                return do_swap_page(vmf);
...
5023:	}
3667:	static vm_fault_t do_pte_missing(struct vm_fault *vmf)
3668:	{
3669:	        if (vma_is_anonymous(vmf->vma))
3670:	                return do_anonymous_page(vmf);
3671:	        else
3672:	                return do_fault(vmf);
3673:	}
4678:	static vm_fault_t do_fault(struct vm_fault *vmf)
4679:	{
4680:	        struct vm_area_struct *vma = vmf->vma;
4681:	        struct mm_struct *vm_mm = vma->vm_mm;
4682:	        vm_fault_t ret;
...
4687:	        if (!vma->vm_ops->fault) {
...
4707:	        } else if (!(vmf->flags & FAULT_FLAG_WRITE))
4708:	                ret = do_read_fault(vmf);
4709:	        else if (!(vma->vm_flags & VM_SHARED))
4710:	                ret = do_cow_fault(vmf);
4711:	        else
4712:	                ret = do_shared_fault(vmf);
...
4719:	        return ret;
4720:	}
4550:	static vm_fault_t do_read_fault(struct vm_fault *vmf)
4551:	{
...
4571:	        ret = __do_fault(vmf);
...
4580:	        return ret;
4581:	}
4181:	static vm_fault_t __do_fault(struct vm_fault *vmf)
4182:	{
4183:	        struct vm_area_struct *vma = vmf->vma;
4184:	        vm_fault_t ret;
...
4207:	        ret = vma->vm_ops->fault(vmf);
...
4234:	        return ret;
4235:	}
vma->vm_ops->fault(vma, &vmf)で処理。
ELF 形式の実行形式や共有ライブラリから機械語命令やデータを読み出す。
linux-6.6.9/mm/memory.c
5369:	struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
5370:	                        unsigned long addr, struct pt_regs *regs)
5371:	{
5372:	        struct vm_area_struct *vma;
...
5377:	        vma = find_vma(mm, addr);
...
5417:	        return vma;
...
5422:	}
linux-6.6.9/mm/mmap.c
1872:	struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
1873:	{
1874:	        unsigned long index = addr;
...
1877:	        return mt_find(&mm->mm_mt, &index, ULONG_MAX);
1878:	}
struct s1 {
   /* 省略 */
};
利用
   struct s1 *p;
   p = malloc( sizeof(struct s1) );
   use( p );
   free( p );
このプログラムを、カーネル内で動かすことを想定してkmalloc() と kfree()
を使って書き換えなさい。ただし、gfp のフラグとしては、GFP_KERNEL を使いなさい。
利用 struct s1 *p; /*回答*/ use( p ); /*回答*/
初期化 /*回答*/ 利用 struct s1 *p; /*回答*/ use( p ); /*回答*/
0x00000000 から 0x00000fff まで
0x00001000 から 0x00001fff まで
0xfffff000 から 0xffffffff まで