annotate paper/xv6.tex @ 49:d3ed28a7964f

update
author mir3636
date Mon, 11 Feb 2019 19:04:56 +0900
parents de8210a1f8e4
children 3179b8daa958
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
11
mir3636
parents:
diff changeset
1 \chapter{xv6 の CbC への書き換え}
48
mir3636
parents: 46
diff changeset
2 xv6 は 2006 年に MIT のオペレーティングシステムコースで教育用の目的として開発されたオペレーティングシステムである。
mir3636
parents: 46
diff changeset
3 xv6 はプロセス、仮想メモリ、カーネルとユーザの分離、割り込み、
mir3636
parents: 46
diff changeset
4 ファイルシステムなどの基本的な Unix の構造を持つにも関わらず、
31
mir3636
parents: 14
diff changeset
5 シンプルで学習しやすい。
mir3636
parents: 14
diff changeset
6
48
mir3636
parents: 46
diff changeset
7 CbC は 継続を中心とした言語であるため状態遷移モデルに落とし込むことができる。
mir3636
parents: 46
diff changeset
8 xv6 を CbC で書き換えることにより、OS の機能の保証が可能となる。
mir3636
parents: 46
diff changeset
9
mir3636
parents: 46
diff changeset
10 また、ハードウェア上でのメタレベルの計算や並列実行を行いたい。
49
mir3636
parents: 48
diff changeset
11 このため、xv6 を ARM\cite{arm} プロセッサを搭載したシングルボードコンピュータである Raspberry pi 用に移植した xv6-rpi を用いて実装する。
48
mir3636
parents: 46
diff changeset
12
mir3636
parents: 46
diff changeset
13 \section{xv6 のモジュール化}
41
mir3636
parents: 40
diff changeset
14
48
mir3636
parents: 46
diff changeset
15 xv6 全体を CbC で書き換えるためには Interface を用いてモジュール化する必要がある。
mir3636
parents: 46
diff changeset
16 xv6 をモジュール化し、CbC で書き換えることができれば、Gears OS の機能を置き換えることもできる。
mir3636
parents: 46
diff changeset
17 図 \ref{fig:xv6Interface} は xv6 の構成図となる。
mir3636
parents: 46
diff changeset
18
mir3636
parents: 46
diff changeset
19 \begin{figure}[ht]
mir3636
parents: 46
diff changeset
20 \begin{center}
mir3636
parents: 46
diff changeset
21 \includegraphics[width=150mm]{fig/xv6.pdf}
mir3636
parents: 46
diff changeset
22 \end{center}
mir3636
parents: 46
diff changeset
23 \caption{モジュール化した xv6 の構成}
mir3636
parents: 46
diff changeset
24 \label{fig:xv6Interface}
mir3636
parents: 46
diff changeset
25 \end{figure}
mir3636
parents: 46
diff changeset
26
40
mir3636
parents: 38
diff changeset
27
33
mir3636
parents: 31
diff changeset
28 \section{xv6 の構成要素}
31
mir3636
parents: 14
diff changeset
29 xv6 はカーネルと呼ばれる形式をとっている。
mir3636
parents: 14
diff changeset
30 カーネルは OS にとって中核となるプログラムである。
mir3636
parents: 14
diff changeset
31 xv6 ではカーネルとユーザープログラムは分離されており、カーネルはプログラムにプロセス管理、メモリ管理、I/O やファイルの管理などのサービスを提供する。
mir3636
parents: 14
diff changeset
32 ユーザープログラムがカーネルのサービスを呼び出す場合、システムコールを用いてユーザー空間からカーネル空間へ入りサービスを実行する。
mir3636
parents: 14
diff changeset
33 カーネルは CPU のハードウェア保護機構を使用して、ユーザー空間で実行されているプロセスが自身のメモリのみアクセスできるように保護している。
mir3636
parents: 14
diff changeset
34 ユーザープログラムがシステムコールを呼び出すと、ハードウェアが特権レベルを上げ、カーネルのプログラムが実行される。
mir3636
parents: 14
diff changeset
35 この特権レベルを持つプロセッサの状態をカーネルモード、特権のない状態をユーザーモードという。
mir3636
parents: 14
diff changeset
36
34
mir3636
parents: 33
diff changeset
37 \subsection{システムコール}
mir3636
parents: 33
diff changeset
38
mir3636
parents: 33
diff changeset
39 ユーザープログラムがカーネルの提供するサービスを呼び出す際にはシステムコールを用いる。
mir3636
parents: 33
diff changeset
40 ユーザープログラムがシステムコールを呼び出すと、トラップが発生する。
mir3636
parents: 33
diff changeset
41 トラップが発生すると、ユーザープログラムは中断され、カーネルに切り替わり処理を行う。
35
mir3636
parents: 34
diff changeset
42 ソースコード \ref{syscall_list} は xv6 のシステムコールのリストである。
34
mir3636
parents: 33
diff changeset
43
35
mir3636
parents: 34
diff changeset
44 \begin{lstlisting}[frame=lrbt,label=syscall_list,caption={\footnotesize xv6 のシステムコールのリスト}]
34
mir3636
parents: 33
diff changeset
45 static int (*syscalls[])(void) = {
mir3636
parents: 33
diff changeset
46 [SYS_fork] =sys_fork,
mir3636
parents: 33
diff changeset
47 [SYS_exit] =sys_exit,
mir3636
parents: 33
diff changeset
48 [SYS_wait] =sys_wait,
mir3636
parents: 33
diff changeset
49 [SYS_pipe] =sys_pipe,
mir3636
parents: 33
diff changeset
50 [SYS_read] =sys_read,
mir3636
parents: 33
diff changeset
51 [SYS_kill] =sys_kill,
mir3636
parents: 33
diff changeset
52 [SYS_exec] =sys_exec,
mir3636
parents: 33
diff changeset
53 [SYS_fstat] =sys_fstat,
mir3636
parents: 33
diff changeset
54 [SYS_chdir] =sys_chdir,
mir3636
parents: 33
diff changeset
55 [SYS_dup] =sys_dup,
mir3636
parents: 33
diff changeset
56 [SYS_getpid] =sys_getpid,
mir3636
parents: 33
diff changeset
57 [SYS_sbrk] =sys_sbrk,
mir3636
parents: 33
diff changeset
58 [SYS_sleep] =sys_sleep,
mir3636
parents: 33
diff changeset
59 [SYS_uptime] =sys_uptime,
mir3636
parents: 33
diff changeset
60 [SYS_open] =sys_open,
mir3636
parents: 33
diff changeset
61 [SYS_write] =sys_write,
mir3636
parents: 33
diff changeset
62 [SYS_mknod] =sys_mknod,
mir3636
parents: 33
diff changeset
63 [SYS_unlink] =sys_unlink,
mir3636
parents: 33
diff changeset
64 [SYS_link] =sys_link,
mir3636
parents: 33
diff changeset
65 [SYS_mkdir] =sys_mkdir,
mir3636
parents: 33
diff changeset
66 [SYS_close] =sys_close,
mir3636
parents: 33
diff changeset
67 };
mir3636
parents: 33
diff changeset
68 \end{lstlisting}
mir3636
parents: 33
diff changeset
69
33
mir3636
parents: 31
diff changeset
70 \subsection{プロセス}
31
mir3636
parents: 14
diff changeset
71 プロセスとは、カーネルが実行するプログラムの単位である。
mir3636
parents: 14
diff changeset
72 xv6 のプロセスは、ユーザー空間メモリとカーネル用のプロセスの状態を持つ空間で構成されている。
mir3636
parents: 14
diff changeset
73 プロセスは独立しており、他のプロセスからメモリを破壊されたりすることはない。
mir3636
parents: 14
diff changeset
74 また、独立していることでカーネルそのものを破壊することもない。
mir3636
parents: 14
diff changeset
75 各プロセスの状態は struct proc によって管理されている。
mir3636
parents: 14
diff changeset
76 プロセスは fork システムコールによって新たに生成される。
mir3636
parents: 14
diff changeset
77 fork は新しく、親プロセスと呼ばれる呼び出し側と同じメモリ内容の、子プロセスと呼ばれるプロセスを生成する。
mir3636
parents: 14
diff changeset
78 fork システムコールは、親プロセスであれば子プロセスのID、子プロセスであれば 0 を返す。
mir3636
parents: 14
diff changeset
79 親プロセスと子プロセスは最初は同じ内容を持っているが、それぞれ異なるメモリ、レジスタで実行されているため、片方のメモリ内容を変更してももう片方に影響はない。
mir3636
parents: 14
diff changeset
80 exit システムコールはプロセスの停止を行い、メモリを解放する。
mir3636
parents: 14
diff changeset
81 wait システムコールは終了した子プロセスのIDを返す。子プロセスが終了するまで待つ。
mir3636
parents: 14
diff changeset
82 exec システムコールは呼び出し元のプロセスのメモリをファイルシステムのファイルのメモリイメージと置き換え実行する。
mir3636
parents: 14
diff changeset
83 ファイルには命令、データなどの配置が指定されたフォーマット通りになっていなければならない。
mir3636
parents: 14
diff changeset
84 xv6 は ELF と呼ばれるフォーマットを扱う。
mir3636
parents: 14
diff changeset
85
33
mir3636
parents: 31
diff changeset
86 \subsection{ファイルディスクリプタ}
31
mir3636
parents: 14
diff changeset
87 ファイルディスクリプタは、カーネルが管理するプロセスが読み書きを行うオブジェクトを表す整数値である。
mir3636
parents: 14
diff changeset
88 プロセスは、ファイル、ディレクトリ、デバイスを開く、または既存のディスクリプタを複製することによって、
mir3636
parents: 14
diff changeset
89 ファイルディスクリプタを取得する。
33
mir3636
parents: 31
diff changeset
90 xv6 はプロセス毎にファイルディスクリプタのテーブルを持っている。
mir3636
parents: 31
diff changeset
91 ファイルディスクリプタは普通、0 が標準入力、1 が標準出力、2 がエラー出力として使われる。
mir3636
parents: 31
diff changeset
92 ファイルディスクリプタのテーブルのエントリを変更することで入出力先を変更することができる。
mir3636
parents: 31
diff changeset
93 1 の標準出力を close し、ファイルを open することでプログラムはファイルに出力することになる。
mir3636
parents: 31
diff changeset
94 ファイルディスクリプタはファイルがどのように接続するか隠すことでファイルへの入出力を容易にしている。
mir3636
parents: 31
diff changeset
95
mir3636
parents: 31
diff changeset
96 \subsection{ファイルシステム}
mir3636
parents: 31
diff changeset
97 xv6 のファイルシステムはバイト配列であるデータファイルとデータファイルおよび他のディレクトリの参照を含むディレクトリを提供する。
mir3636
parents: 31
diff changeset
98 ディレクトリは root と呼ばれる特別なディレクトリから始まるツリーを形成している。
mir3636
parents: 31
diff changeset
99 絶対パスである "/dir1/dir2/file1" というパスは root ディレクトリ内の dir1 という名前のディレクトリ内の dir2 という名前のディレクトリ内の file というデータファイルを指す。
mir3636
parents: 31
diff changeset
100 相対パスである "dir2/file2" のようなパスは、現在のディレクトリ内の dir2 という名前のディレクトリ内の file というデータファイルを指す。
mir3636
parents: 31
diff changeset
101
14
mir3636
parents: 11
diff changeset
102 \section{xv6-rpi の CbC 対応}
11
mir3636
parents:
diff changeset
103
35
mir3636
parents: 34
diff changeset
104 オリジナルの xv6 は x86 アーキテクチャで実装されたものだが、xv6-rpi は Raspberry Pi 用に実装されたものである。
11
mir3636
parents:
diff changeset
105
mir3636
parents:
diff changeset
106 xv6-rpi を CbC で書き換えるために、
35
mir3636
parents: 34
diff changeset
107 GCC 上で実装した CbC コンパイラを ARM 向けに build し xv6-rpi をコンパイルした。
mir3636
parents: 34
diff changeset
108 これにより、 xv6-rpi を CbC で書き換えることができるようになった。
mir3636
parents: 34
diff changeset
109
36
mir3636
parents: 35
diff changeset
110 \section{システムコールの書き換え}
35
mir3636
parents: 34
diff changeset
111 ソースコード \ref{syscall} は syscall() におけるシステムコールの呼び出しを行うコードである。
49
mir3636
parents: 48
diff changeset
112 システムコールはソースコード \ref{syscall_list} の関数のリストの番号から呼び出される。
35
mir3636
parents: 34
diff changeset
113 CbC でも同様に num で指定された番号の cbccodes のリストの Code Gear へ goto する。
49
mir3636
parents: 48
diff changeset
114 ソースコード \ref{syscall} 6行目でトラップフレームからシステムコールの番号を取得する。
mir3636
parents: 48
diff changeset
115 通常のシステムコールであればソースコード \ref{syscall} 13行目からの分岐へ入るが、
mir3636
parents: 48
diff changeset
116 cbc システムコールであれば ソースコード \ref{syscall} 8行目へ入り、goto によって遷移する。
35
mir3636
parents: 34
diff changeset
117 引数に持つ cbc\_ret は 継続した先でトラップに戻ってくるための Code Gear である。
mir3636
parents: 34
diff changeset
118
49
mir3636
parents: 48
diff changeset
119
35
mir3636
parents: 34
diff changeset
120 \begin{lstlisting}[frame=lrbt,label=syscall,caption={\footnotesize syscall()}]
49
mir3636
parents: 48
diff changeset
121 void syscall(void)
mir3636
parents: 48
diff changeset
122 {
mir3636
parents: 48
diff changeset
123 int num;
mir3636
parents: 48
diff changeset
124 int ret;
mir3636
parents: 48
diff changeset
125
mir3636
parents: 48
diff changeset
126 num = proc->tf->r0;
mir3636
parents: 48
diff changeset
127
35
mir3636
parents: 34
diff changeset
128 if((num >= NELEM(syscalls)) && (num <= NELEM(cbccodes)) && cbccodes[num]) {
mir3636
parents: 34
diff changeset
129 proc->cbc_arg.cbc_console_arg.num = num;
mir3636
parents: 34
diff changeset
130 goto (cbccodes[num])(cbc_ret);
mir3636
parents: 34
diff changeset
131 }
mir3636
parents: 34
diff changeset
132
mir3636
parents: 34
diff changeset
133 if((num > 0) && (num <= NELEM(syscalls)) && syscalls[num]) {
mir3636
parents: 34
diff changeset
134 ret = syscalls[num]();
mir3636
parents: 34
diff changeset
135
mir3636
parents: 34
diff changeset
136 // in ARM, parameters to main (argc, argv) are passed in r0 and r1
mir3636
parents: 34
diff changeset
137 // do not set the return value if it is SYS_exec (the user program
mir3636
parents: 34
diff changeset
138 // anyway does not expect us to return anything).
mir3636
parents: 34
diff changeset
139 if (num != SYS_exec) {
mir3636
parents: 34
diff changeset
140 proc->tf->r0 = ret;
mir3636
parents: 34
diff changeset
141 }
49
mir3636
parents: 48
diff changeset
142 } else {
mir3636
parents: 48
diff changeset
143 cprintf("%d %s: unknown sys call %d\n", proc->pid, proc->name, num);
mir3636
parents: 48
diff changeset
144 proc->tf->r0 = -1;
35
mir3636
parents: 34
diff changeset
145 }
49
mir3636
parents: 48
diff changeset
146 }
35
mir3636
parents: 34
diff changeset
147 \end{lstlisting}
mir3636
parents: 34
diff changeset
148
49
mir3636
parents: 48
diff changeset
149 ソースコード \ref{cbc_read} は、
mir3636
parents: 48
diff changeset
150 read システムコールであるソースコード \ref{sys_read} を CbC で書き換えたコードである。
mir3636
parents: 48
diff changeset
151 CbC は C の関数を呼び出すことも出来るため、書き換えたい部分だけを書き換えることができる。
mir3636
parents: 48
diff changeset
152 Code Gear であるため、ソースコード \ref{cbc_read} 7、9行目のように関数呼び出しではなく goto による継続となる。
37
mir3636
parents: 36
diff changeset
153
49
mir3636
parents: 48
diff changeset
154 \begin{lstlisting}[frame=lrbt,label=sys_read,caption={\footnotesize cbc\_read システムコール}]
37
mir3636
parents: 36
diff changeset
155 __code cbc_read(__code (*next)(int ret)){
mir3636
parents: 36
diff changeset
156 struct file *f;
mir3636
parents: 36
diff changeset
157 int n;
mir3636
parents: 36
diff changeset
158 char *p;
mir3636
parents: 36
diff changeset
159
mir3636
parents: 36
diff changeset
160 if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) {
mir3636
parents: 36
diff changeset
161 goto next(-1);
mir3636
parents: 36
diff changeset
162 }
mir3636
parents: 36
diff changeset
163 goto cbc_fileread(f, p, n, next);
mir3636
parents: 36
diff changeset
164 }
49
mir3636
parents: 48
diff changeset
165 \end{lstlisting}
37
mir3636
parents: 36
diff changeset
166
49
mir3636
parents: 48
diff changeset
167 \begin{lstlisting}[frame=lrbt,label=sys_read,caption={\footnotesize read システムコール}]
37
mir3636
parents: 36
diff changeset
168 int sys_read(void)
mir3636
parents: 36
diff changeset
169 {
mir3636
parents: 36
diff changeset
170 struct file *f;
mir3636
parents: 36
diff changeset
171 int n;
mir3636
parents: 36
diff changeset
172 char *p;
mir3636
parents: 36
diff changeset
173
mir3636
parents: 36
diff changeset
174 if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) {
mir3636
parents: 36
diff changeset
175 return -1;
mir3636
parents: 36
diff changeset
176 }
mir3636
parents: 36
diff changeset
177
mir3636
parents: 36
diff changeset
178 return fileread(f, p, n);
mir3636
parents: 36
diff changeset
179 }
mir3636
parents: 36
diff changeset
180 \end{lstlisting}
mir3636
parents: 36
diff changeset
181
mir3636
parents: 36
diff changeset
182 継続で Code Gear 間を遷移するため関数呼び出しとは違い元の関数には戻ってこない。
49
mir3636
parents: 48
diff changeset
183 このため、書き換えの際には ソースコード \ref{cbcfileread} のように分割する必要がある。
38
mir3636
parents: 37
diff changeset
184 プロセスの状態を持つ struct proc は大域変数であるため Code Gear を用いるために必要なパラメーターを cbc\_arg として持たせることにした。
mir3636
parents: 37
diff changeset
185 継続を行う際には必要なパラメーターをここに格納する。
37
mir3636
parents: 36
diff changeset
186
49
mir3636
parents: 48
diff changeset
187 \lstinputlisting[label=cbcfileread, caption=fileread の CbC 書き換えの例]{./src/fileread.cbc}
mir3636
parents: 48
diff changeset
188 \lstinputlisting[label=fileread, caption=書き換え前の fileread]{./src/fileread.c}
38
mir3636
parents: 37
diff changeset
189
49
mir3636
parents: 48
diff changeset
190 CbC では cbc\_devsw を定義しておりソースコード \ref{cbcreadi} 11行目で次の Code Gear へと継続する。
41
mir3636
parents: 40
diff changeset
191
49
mir3636
parents: 48
diff changeset
192 \lstinputlisting[label=cbcreadi, caption=readi の CbC 書き換えの例]{./src/readi.cbc}
45
mir3636
parents: 42
diff changeset
193 \lstinputlisting[label=readi, caption=readi の CbC 書き換えの例]{./src/readi.c}
46
mir3636
parents: 45
diff changeset
194
48
mir3636
parents: 46
diff changeset
195 ソースコード \ref{consoleread} 11、50、行目 では sleep へ継続する際、戻るべき継続先を一緒に送ることで、
mir3636
parents: 46
diff changeset
196 sleep から consoleread に戻ってくることができる。
46
mir3636
parents: 45
diff changeset
197
45
mir3636
parents: 42
diff changeset
198 \lstinputlisting[label=consoleread, caption=consoleread の CbC 書き換えの例]{./src/console.c}
41
mir3636
parents: 40
diff changeset
199
mir3636
parents: 40
diff changeset
200
38
mir3636
parents: 37
diff changeset
201 xv6 のシステムコールの関数を CbC で書き換え、QEMU 上で動作させることができた。
mir3636
parents: 37
diff changeset
202
46
mir3636
parents: 45
diff changeset
203 \section{xv6 の CbC 書き換えによる考察}
38
mir3636
parents: 37
diff changeset
204
41
mir3636
parents: 40
diff changeset
205 CbC は Code Gear 間の遷移は goto による継続で行われるため、
mir3636
parents: 40
diff changeset
206 状態遷移ベースでのプログラムに適している。
mir3636
parents: 40
diff changeset
207 xv6 を CbC で書き換えることにより、OS のプログラムを状態遷移モデルに落とし込むことができる。
mir3636
parents: 40
diff changeset
208 これにより状態遷移系のモデル検査が可能となる。
38
mir3636
parents: 37
diff changeset
209
41
mir3636
parents: 40
diff changeset
210 また、CbC は Agda に変換できるように設計されているため CbC で記述された実行可能なプログラムが、
mir3636
parents: 40
diff changeset
211 そのまま Agda による定理証明ができる。
mir3636
parents: 40
diff changeset
212
mir3636
parents: 40
diff changeset
213 これらのため、CbC で記述された実行可能な OS そのものがモデル検査と定理証明が可能となり、
mir3636
parents: 40
diff changeset
214 OS の信頼性が保証できる。
mir3636
parents: 40
diff changeset
215
48
mir3636
parents: 46
diff changeset
216 \section{Raspberry Pi}
mir3636
parents: 46
diff changeset
217 Raspberry Pi は ARM\cite{arm} プロセッサを搭載したシングルボードコンピュータである。
mir3636
parents: 46
diff changeset
218 教育用のコンピューターとして開発されたもので、低価格であり、小型であるため使い勝手が良い。
mir3636
parents: 46
diff changeset
219 ストレージにハードディスクや SSD を使用するのではなく、SD カードを用いる。
mir3636
parents: 46
diff changeset
220 HDMI 出力や USB ポートも備えており、開発に最適である。
mir3636
parents: 46
diff changeset
221
mir3636
parents: 46
diff changeset
222 Raspberry Pi には Raspberry Pi 3、Raspberry Pi 2、Raspberry Pi 1、Raspberry Pi Zero といったバージョンが存在する。
mir3636
parents: 46
diff changeset
223