├── fig
├── as.pdf
├── os.pdf
├── race.pdf
├── inode.pdf
├── switch.pdf
├── deadlock.pdf
├── fslayer.pdf
├── fslayout.pdf
├── mkernel.pdf
├── xv6_layout.pdf
├── processlayout.pdf
├── riscv_address.pdf
├── riscv_pagetable.pdf
├── mkernel.svg
├── os.svg
├── race.svg
├── fslayer.svg
├── deadlock.svg
├── as.svg
├── switch.svg
├── fslayout.svg
└── bufrace.svg
├── font
├── MinionPro-It.otf
├── MinionPro-Bold.otf
├── MinionPro-BoldIt.otf
├── MinionPro-Regular.otf
├── MinionPro-Semibold.otf
├── MinionPro-SemiboldIt.otf
├── README
└── LucidaSans-Typewriter83.afm
├── .gitignore
├── sum.tex
├── Makefile
├── README.md
├── LICENSE
├── acks.tex
├── book.tex
├── lineref
├── book.bib
├── lock2.tex
├── tr2tex
├── book.mac
├── first.tex
└── mem.tex
/fig/as.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/as.pdf
--------------------------------------------------------------------------------
/fig/os.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/os.pdf
--------------------------------------------------------------------------------
/fig/race.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/race.pdf
--------------------------------------------------------------------------------
/fig/inode.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/inode.pdf
--------------------------------------------------------------------------------
/fig/switch.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/switch.pdf
--------------------------------------------------------------------------------
/fig/deadlock.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/deadlock.pdf
--------------------------------------------------------------------------------
/fig/fslayer.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/fslayer.pdf
--------------------------------------------------------------------------------
/fig/fslayout.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/fslayout.pdf
--------------------------------------------------------------------------------
/fig/mkernel.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/mkernel.pdf
--------------------------------------------------------------------------------
/fig/xv6_layout.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/xv6_layout.pdf
--------------------------------------------------------------------------------
/fig/processlayout.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/processlayout.pdf
--------------------------------------------------------------------------------
/fig/riscv_address.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/riscv_address.pdf
--------------------------------------------------------------------------------
/font/MinionPro-It.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-It.otf
--------------------------------------------------------------------------------
/fig/riscv_pagetable.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/fig/riscv_pagetable.pdf
--------------------------------------------------------------------------------
/font/MinionPro-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-Bold.otf
--------------------------------------------------------------------------------
/font/MinionPro-BoldIt.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-BoldIt.otf
--------------------------------------------------------------------------------
/font/MinionPro-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-Regular.otf
--------------------------------------------------------------------------------
/font/MinionPro-Semibold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-Semibold.otf
--------------------------------------------------------------------------------
/font/MinionPro-SemiboldIt.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugawara-bbbb/xv6-riscv-book/HEAD/font/MinionPro-SemiboldIt.otf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pdf
2 | *.*~
3 | *.ps
4 | latex.out/
5 | xv6-riscv-src/
6 | *.aux
7 | *.idx
8 | *.ilg
9 | *.ind
10 | *.log
11 | *.toc
12 | *.bbl
13 | *.blg
14 | *.out
15 |
--------------------------------------------------------------------------------
/font/README:
--------------------------------------------------------------------------------
1 | These fonts are taken from
2 |
3 | /home/am8/rsc/font/Adobe/TypeClassics/MinionPro
4 | /home/am8/rsc/font/BellLabs
5 |
6 | They are copyrighted material used under license
7 | and cannot be redistributed.
8 |
9 |
--------------------------------------------------------------------------------
/sum.tex:
--------------------------------------------------------------------------------
1 | \chapter{まとめ}
2 | \label{CH:SUM}
3 |
4 | このテキストは,オペレーティングシステムの 1 つである xv6 を 1 行ずつ学
5 | ぶことで,オペレーティングシステムの主要なアイディアを紹介しました.
6 | 数行のコードが主要なアイディアの本質を体現することがあるので (例: コン
7 | テキストスイッチ,ユーザとカーネルの境界,ロックなど),どの行も重要で
8 | す.また,数行のコードは,オペレーティングシステムのある特定のアイディ
9 | アをどうやって実装するかを表しており,かつそれらは簡単に別の方法に取り
10 | 替えることができます(例: より良いスケジューリングアルゴリズム,より良
11 | いファイルを表すディスク上のデータ構造,並行するトランザクションを可能
12 | とするより良いロギングなど).それらのアイディアの説明を,
13 | とても成功しているシステムコールインタフェースである Unix インタフェー
14 | スの文脈で行いました.しかし,それらのアイディアは,他のオペ
15 | レーティングシステムでも役に立つはずです.
16 |
17 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SRC=xv6-riscv-src/
2 |
3 | T=latex.out
4 |
5 | TEX=\
6 | $(T)/acks.tex\
7 | $(T)/unix.tex\
8 | $(T)/first.tex\
9 | $(T)/mem.tex\
10 | $(T)/trap.tex\
11 | $(T)/lock.tex\
12 | $(T)/sched.tex\
13 | $(T)/fs.tex\
14 | $(T)/lock2.tex\
15 | $(T)/sum.tex\
16 |
17 | all: book.pdf
18 | .PHONY: all
19 |
20 | $(T)/%.tex: %.tex
21 | mkdir -p latex.out
22 | ./lineref $(notdir $@) $(SRC) > $@
23 |
24 | src:
25 | if [ ! -d $(SRC) ]; then \
26 | git clone git@github.com:mit-pdos/xv6-riscv.git $(SRC) ; \
27 | fi; \
28 | cd $(SRC); git pull; true
29 |
30 | book.pdf: src book.tex $(TEX)
31 | ptex2pdf -l book.tex
32 | bibtex book
33 | ptex2pdf -l book.tex
34 | ptex2pdf -l book.tex
35 |
36 | clean:
37 | rm -f book.aux book.idx book.ilg book.ind book.log\
38 | book.toc book.bbl book.blg book.out
39 | rm -rf latex.out
40 | # rm -rf $(SRC)
41 |
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # この文書について
2 |
3 | 以下の Web ページで,コンパイル済みの PDF を配布しています: [電気通信大学・菅原研究室](https://www.sugawara-lab.jp/lecture.html).
4 |
5 | この文書は,
6 | https://github.com/mit-pdos/xv6-riscv-book
7 | で公開されている RISC-V 版 xv6 のテキストを和訳したものです.
8 |
9 | 対応する RISC-V 版 xv6 のソースコードは以下です.
10 | https://github.com/mit-pdos/xv6-riscv
11 |
12 | # ビルド方法
13 |
14 | プリプロセッサである lineref が,ファイル中の lineref コマンド(例: `\lineref{kernel/proc.h:/^struct.context/}`)を見つけ,ソースコードを参照して行番号を入力する.生成したコードは latex.out/ に入る.
15 |
16 | lineref のために,ソースコードの場所を指定する必要がある.デフォルトでは,カレントディレクトリに xv6-riscv-src という名前でソースコードのディレクトリが必要.以下のようにしてビルドできる.
17 |
18 | ``` shell
19 | % cd xv6-riscv-book
20 | % git clone https://github.com/mit-pdos/xv6-riscv.git xv6-riscv-src
21 | % make
22 | ```
23 |
24 | # ビルドに関する変更点
25 |
26 | 日本語を扱うために,pdflatex から platex に変更した.それに伴い,Makefile と book.tex の微調整を行った.
27 |
28 | # ライセンス
29 |
30 | 元の文書は MIT ライセンスで公開されています(LICENSE を参照).ここで公開する訳文も同ライセンスに従います.
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The xv6 book sources are:
2 |
3 | Copyright (c) 2006-2019 Russ Cox, Frans Kaashoek, and Robert Morris,
4 | Massachusetts Institute of Technology
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this source and associated documentation files (the "Book"), to deal in the Book
8 | without restriction, including without limitation the rights to use, copy,
9 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Book,
10 | and to permit persons to whom the Book is furnished to do so, subject to the
11 | following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Book.
15 |
16 | THE BOOK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
17 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE BOOK OR THE USE OR OTHER DEALINGS IN THE BOOK.
22 |
23 |
--------------------------------------------------------------------------------
/acks.tex:
--------------------------------------------------------------------------------
1 | \chapter*{序文と謝辞}
2 |
3 | この文書は,オペレーティングシステムの講義用テキストのドラフトで
4 | す.xv6 というカーネルを例題にして,オペレーティングシステムの主なコン
5 | セプトを説明します.xv6 は,Dennis Ritchie と Ken Thompson による Unix
6 | Version 6 (v6)~\cite{unix} の再実装です.xv6 は,v6 の構造とスタイルを
7 | ゆるく踏襲しますが,ANSI C~\cite{kernighan} で記述され,マルチコア
8 | の RISC-V~\cite{riscv} で動作します.
9 |
10 | このテキストは,xv6 のソースコードと一緒に読むことを想定しています.
11 | ``John Lions's Commentary on UNIX 6th Edition''~\cite{lions}
12 | に触発されたアプローチです.xv6 手を動かす実習課題を含む
13 | v6 と xv6 オンライン教材へのリンクは,
14 | \url{https://pdos.csail.mit.edu/6.828}
15 | を見てください.
16 |
17 | 私たちはこのテキストを MIT のオペレーティングシステムの講義である6.828 で
18 | 利用しました.xv6 に直接的・間接的に貢献してくれた教員,ティーチングア
19 | シスタント,そして 6.828 の受講生に感謝します.Austin
20 | Clements と Nickolai Zeldovich には特に感謝をします.最後に,
21 | テキストのバグや改善のための助言をメールを送ってくれた以下の人たちに感謝します:
22 | %
23 | Abutalib Aghayev, Sebastian Boehm, Anton Burtsev, Raphael Carvalho,
24 | Tej Chajed, Rasit Eskicioglu, Color Fuzzy, Giuseppe, Tao Guo, Robert
25 | Hilderman, Wolfgang Keller, Austin Liew, Pavan Maddamsetti, Jacek
26 | Masiulaniec, Michael McConville, miguelgvieira, Mark Morrissey, Harry
27 | Pan, Askar Safin, Salman Shah, Ruslan Savchenko, Pawel Szczurko,
28 | Warren Toomey, tyfkda, tzerbib, Xi Wang, and Zou Chang Wei.
29 |
30 | 間違いを見つけたり,改善のための提案があれば,
31 | Frans Kaashoek と Robert Morris (kaashoek,rtm@csail.mit.edu)
32 | にメールしてください.
33 |
34 |
--------------------------------------------------------------------------------
/book.tex:
--------------------------------------------------------------------------------
1 | \documentclass[a4paper, 12pt, oneside]{jsbook}
2 | \usepackage[driver=dvipdfmx,truedimen,margin=0cm]{geometry}
3 | \usepackage[T1]{fontenc}
4 | \usepackage{times}
5 | \usepackage{listings}
6 | \usepackage[dvipdfmx]{graphicx}
7 | \usepackage{xcolor}
8 | % \usepackage{showidx}
9 | \usepackage{imakeidx}
10 | \usepackage{booktabs}
11 | \usepackage{url}
12 | \usepackage{etoolbox} % for showidx
13 | \usepackage{fullpage}
14 | \usepackage{soul}
15 |
16 | \usepackage{hyperref} % should be last
17 |
18 | % One space after periods
19 | \frenchspacing
20 |
21 | \hypersetup{pdfauthor={Russ Cox, Frans Kaashoek, Robert Morris},
22 | pdftitle={xv6: a simple, Unix-like teaching operating system},}
23 |
24 | \lstset{basicstyle=\small\ttfamily}
25 | \lstset{morecomment=[is]{[[[}{]]]}}
26 | \lstset{escapeinside={(*@}{@*)}}
27 | \lstset{xleftmargin=5.0ex}
28 |
29 | \newcommand{\github}{https://github.com/mit-pdos/xv6-riscv/blob/riscv/}
30 |
31 | \newcommand{\fileref}[1]{\href{\github/#1}{\small{(#1)}}}
32 | \newcommand{\lineref}[2]{\href{\github/#1\#L#2}{\small{(#1:#2)}}}
33 | \newcommand{\linerefs}[3]{\href{\github/#1\#L#2-L#3}{\small(#1:#2-#3)}}
34 |
35 | \newcommand{\indextext}[1]{\textit{#1}\index{#1}}
36 | \newcommand{\indexcode}[1]{\lstinline{#1}\index{#1@\lstinline{#1}}}
37 |
38 | %% editing markup
39 | \newcommand{\insertnote}[3]{\noindent\textcolor{#1}{\textbf{#2:} #3}}
40 | \newcommand{\note}[1]{\insertnote{blue}{NOTE}{#1}}
41 | \newcommand{\rtm}[1]{\insertnote{red}{RTM}{#1}}
42 | \newcommand{\mfk}[1]{\insertnote{red}{MFK}{#1}}
43 | %% for publishing book without notes
44 | %\renewcommand{\insertnote}[3]{}
45 |
46 | \title{\textbf{xv6: シンプルで Unix 風な\\教育用オペレーティングシステム}}
47 | \author{Russ Cox \hspace{3em} Frans Kaashoek \hspace{3em} Robert Morris\\ \vspace{1em} \\菅原 健(訳)}
48 |
49 | \makeindex
50 |
51 | \begin{document}
52 |
53 | \maketitle
54 |
55 | \tableofcontents
56 |
57 | \input{latex.out/acks}
58 | \input{latex.out/unix}
59 | \input{latex.out/first}
60 | \input{latex.out/mem}
61 | \input{latex.out/trap}
62 | \input{latex.out/lock}
63 | \input{latex.out/sched}
64 | \input{latex.out/fs}
65 | \input{latex.out/lock2}
66 | \input{latex.out/sum}
67 |
68 | {
69 | % The following prevents latex from splitting a bibliography entry with a page
70 | % break
71 | \interlinepenalty=10000
72 | % Since we're using natbib in numbers mode, we don't need plainnat,
73 | % which exists to feed authors and years back in to natbib. As a
74 | % result, it complains about entries without years, which we don't
75 | % care about.
76 | %\bibliographystyle{plainnat}
77 | \bibliographystyle{plain}
78 | \bibliography{book}
79 | }
80 |
81 | \printindex
82 |
83 | \end{document}
84 |
--------------------------------------------------------------------------------
/lineref:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.7
2 |
3 | import os
4 | import re
5 | import sys
6 |
7 | if len(sys.argv) < 2:
8 | print("error: too few arguments", file=sys.stderr)
9 |
10 | path = sys.argv[2]
11 |
12 | def lookup_regex(fname, pat1, pat2=None):
13 | fname = fname.replace("\_", "_")
14 | p1 = re.compile(pat1)
15 | if pat2 != None:
16 | p2 = re.compile(pat2)
17 | else:
18 | p2 = None
19 | cnt = 1
20 | i = None
21 | j = None
22 | p = p1
23 | try:
24 | with open(path+'/'+fname) as f:
25 | line = f.readline()
26 | while line:
27 | m = p.search(line)
28 | if m != None:
29 | if p2 == None:
30 | return (cnt, None)
31 | else:
32 | if i == None:
33 | i = cnt
34 | p = p2
35 | else:
36 | j = cnt
37 | return (i, j)
38 | cnt += 1
39 | line = f.readline()
40 | if i == None:
41 | print("%s: cannot find pat %s" % (fname, p1), file=sys.stderr)
42 | else:
43 | print("%s: cannot find pat %s" % (fname, p2), file=sys.stderr)
44 | return (None, None)
45 | except IOError:
46 | print("error: cannot open %s" % fname, file=sys.stderr)
47 | return (None, None)
48 |
49 | def lineref(l):
50 | # file:/pattern/delta
51 | p = re.compile(r'\\lineref{(.*):\/(.*)\/([+-]?\d+)?}')
52 | m = p.search(l)
53 | if m != None:
54 | f = m.groups()[0]
55 | (i, j) = lookup_regex(f, m.groups()[1])
56 | delta = 0
57 | if m.groups()[2] != None:
58 | delta = int(m.groups()[2])
59 | if i != None:
60 | l = p.sub(r'\\lineref{%s}{%s}' % (f,str(i+delta)), l)
61 | print(l, end="")
62 | return
63 | # file:/line/
64 | p = re.compile(r'\\lineref\{(.*):(\d+)\}')
65 | m = p.search(l)
66 | if m != None:
67 | f = m.groups()[0]
68 | n = m.groups()[1]
69 | l = p.sub(r'\\lineref{%s}{%s}' % (f, n), l)
70 | print(l, end="")
71 | return
72 | # file:/pattern/delta,pattern/delta
73 | p = re.compile(r'\\linerefs{(.*):\/(.*)\/([+-]?\d+)?,\/(.*)\/([+-]?\d+)?}')
74 | m = p.search(l)
75 | if m != None:
76 | f = m.groups()[0]
77 | (i, j) = lookup_regex(f, m.groups()[1], m.groups()[3])
78 | delta1 = 0
79 | if m.groups()[2] != None:
80 | delta1 = int(m.groups()[2])
81 | delta2 = 0
82 | if m.groups()[4] != None:
83 | delta2 = int(m.groups()[4])
84 | if i != None and j != None:
85 | l = p.sub(r'\\linerefs{%s}{%s}{%s}' % (f, str(i+delta1), str(j+delta2)), l)
86 | print(l, end="")
87 | return
88 | print(l, end="")
89 |
90 | with open(sys.argv[1]) as f:
91 | line = f.readline()
92 | while line:
93 | lineref(line)
94 | line = f.readline()
95 |
--------------------------------------------------------------------------------
/book.bib:
--------------------------------------------------------------------------------
1 | @book{riscv,
2 | author = {Patterson, David and Waterman, Andrew},
3 | title = {The {RISC-V} Reader: an open architecture Atlas},
4 | year = {2017},
5 | isbn = {099924910X, 9780999249109},
6 | publisher = {Strawberry Canyon},
7 | }
8 |
9 | @book{lions,
10 | author = {John Lions},
11 | title = {Commentary on UNIX 6th Edition},
12 | year = 2000,
13 | publisher = {Peer to Peer Communications},
14 | isbn = {1-57398-013-7},
15 | }
16 |
17 | @article{unix,
18 | author = {Ritchie, Dennis M. and Thompson, Ken},
19 | title = {The {UNIX} Time-sharing System},
20 | journal = {Commun. ACM},
21 | issue_date = {July 1974},
22 | volume = {17},
23 | number = {7},
24 | month = jul,
25 | year = {1974},
26 | pages = {365--375},
27 | numpages = {11},
28 | url = {http://doi.acm.org/10.1145/361011.361061},
29 | doi = {10.1145/361011.361061},
30 | publisher = {ACM},
31 | }
32 |
33 | @book{knuth,
34 | author = {Knuth, Donald},
35 | title = {Fundamental Algorithms. The Art of Computer Programming. (Second ed.)},
36 | year = 1997,
37 | volume = 1,
38 | publisher = Addison-Wesley,
39 | isbn = {0-201-89683-4},
40 | }
41 |
42 | @document{riscv:priv,
43 | title = {The {RISC-V} instruction set manual: privileged architecture},
44 | editor = {Andrew Waterman and Krste Asanovic},
45 | year = 2017,
46 | howpublished = {\url{https://content.riscv.org/wp-content/uploads/2017/05/riscv-privileged-v1.10.pdf}}
47 | }
48 |
49 | @document{riscv:user,
50 | title = {The {RISC-V} instruction set manual: user-level {ISA}},
51 | editor = {Andrew Waterman and Krste Asanovic},
52 | year = 2017,
53 | howpublished = {\url{https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf}},
54 | }
55 |
56 |
57 | @book{kernighan,
58 | author = {Kernighan, Brian W.},
59 | editor = {Ritchie, Dennis M.},
60 | title = {The C Programming Language},
61 | year = {1988},
62 | isbn = {0131103709},
63 | edition = {2nd},
64 | publisher = {Prentice Hall Professional Technical Reference},
65 | }
66 |
67 | @document{u54,
68 | author = {SiFive},
69 | title = {SiFive {FU540-C000} manual},
70 | howpublished={\url{https://sifive.cdn.prismic.io/sifive%2F590bbcb6-598e-4ed8-b5d3-88c2c7458ebf_u54-core-complex-manual-v19.05.pdf}},
71 | year = "2018",
72 | }
73 |
74 | @document{virtio,
75 | author = {{OASIS} Open},
76 | title = {Virtual {I/O} Device ({VIRTIO}) Version 1.0},
77 | year = "2016",
78 | month = "March",
79 | howpublished={\url{http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html}},
80 | }
81 |
82 |
83 | @document{dijkstra65,
84 | author = {Edsger Dijkstra},
85 | title = {Cooperating Sequential Processes},
86 | year = "1965",
87 | howpublished={\url{https://www.cs.utexas.edu/users/EWD/transcriptions/EWD01xx/EWD123.html}},
88 | }
89 |
90 | @document{ns16550a,
91 | author = {Martin Michael and Daniel Durich},
92 | year = "1987",
93 | title = {The {NS16550A}: {UART} Design and Application Considerations},
94 | howpublished = {\url{http://bitsavers.trailing-edge.com/components/national/_appNotes/AN-0491.pdf}},
95 | }
96 |
97 | @article{boehm04,
98 | author = {Boehm, Hans-J},
99 | title = {Threads cannot be implemented as a library},
100 | journal = {ACM PLDI Conference},
101 | year = {2005},
102 | }
103 |
--------------------------------------------------------------------------------
/lock2.tex:
--------------------------------------------------------------------------------
1 | \chapter{並行性・再び}
2 | \label{CH:LOCK2}
3 |
4 | 良い並列性能,並列処理のおける正しさ,および理解しやすいコードの3つを満
5 | たすことは,カーネル設計における大きな課題です.素直なロックの使用
6 | はよい方法ですが,いつもできるわけではありません.この章で
7 | は,xv6 がややこしい方法でロックを使わなくてはならなかった例と,
8 | ロック的だがロックではない方法を使う例を紹介します.
9 |
10 | \section{ロックのパターン}
11 |
12 | キャッシュされたアイテムをロックするのは難しいことがあります.たとえば,
13 | ファイルシステムのブロックキャッシュ
14 | \lineref{kernel/bio.c:/^struct/} は,ディスクブロッ
15 | クのコピーを \texttt{NBUF} 個まで持つことができます.あるディスクブロッ
16 | クに対し,キャッシュ内のコピーが最大でも 1 個であることは非常に重要です.
17 | そうしないと,同じブロックを示す異なるキャッシュが食い違うかもしれない
18 | からです.キャッシュされたブロックは \texttt{buf} 構造
19 | 体 \lineref{kernel/buf.h:1} に入ります.\texttt{buf} 構造体にあるロック用のフィー
20 | ルドが,あるブロックを一度にひとつのプロセスしか使えないことを保証しま
21 | す.しかし,ロックだけでは足りません.ブロックがそもそもキャッシュに入っ
22 | てなかったらどうなるでしょうか?(そのブロックはキャッシュされていない
23 | ので)\texttt{buf} 構造体は存在せず,よってロックそのものが存在しませ
24 | ん.xv6 はそのような状況を取り扱うために,キャッシュブロックの素性と追
25 | 加のロック (\texttt{bcache.lock}) を関連づけます.ブロックがキャッシュ
26 | されているかどうかをチェックしなくてはならないコード(たとえ
27 | ば \texttt{bget} \lineref{kernel/bio.c:/^bget/})や,複数のキャッシュブロッ
28 | クを書き換えるコードは \texttt{bcache.lock} を取得しなくてはなりません.
29 | 必要なブロックや \texttt{buf} 構造体を見つけたら,そのコード
30 | は \texttt{bcache.lock} を解放し,それからブロック固有のロックを取
31 | 得します.アイテムの集合に対するロック 1 つと,各アイテムご
32 | とにロックの組み合わせは頻出のパターンです.
33 |
34 | 通常,ロックを取得した関数がその解放も行います.しかし,より正確な理解
35 | は,アトミックに見えなくてはならないシーケンスの始まりでロックを取得し,
36 | そのシーケンスの終わりにロックを解放する,というものです.もしシーケン
37 | スの始まりと終わりが別の関数,別のスレッド,もしくは別の CPU にあるなら
38 | ば,ロックの取得・解放もそうしなくてはなりません.ロックの機能は他の利
39 | 用者を待たせることであって,あるデータを特定のエージェントに紐付けする
40 | ことではありません.1 つの例は \texttt{yield} \lineref{kernel/proc.c:/^yield/} にお
41 | ける \texttt{acquire} で,\texttt{acquire} を呼んだプロセスでは
42 | なく,スケジューラスレッドで解放されます.さらに別の例
43 | は \texttt{ilock} \lineref{kernel/fs.c:/^ilock/} における\texttt{acquiresleep} で
44 | す.このコードはディスクを読みながらスリープすることがよくあり,すると
45 | 別の CPU で起床します.すなわち,ロックの解放が別の CPU で行われます.
46 |
47 | オブジェクトには,そのオブジェクト自身に埋め込んであるロックによって保
48 | 護されているものがあり,そのようなオブジェクトを解放(消去) するのは
49 | デリケートな仕事です.正しく解放するには,ロックだけでは足りない
50 | からです.問題が起きるのは,別のスレッドがそのオブジェクトを使用するた
51 | めに \texttt{acquire} で待っていた場合です.オブジェクトの解放は,ロッ
52 | クの解放を意味するので,待っていたスレッドが誤動作してしまいます.解決
53 | 法の1つは,そのオブジェクトへの参照を数えておき,最後の参照が消滅した時
54 | 点でのみ解放を行うというものです.\texttt{pipeclose} \lineref{kernel/pipe.c:/^pipeclose/} が
55 | その例です.\texttt{pi->readopen} と \texttt{pi->writeopen} が,そのパイプを参
56 | 照するファイルディスクリプタが存在するかどうかを追跡しています.
57 |
58 | % sleep locks.
59 | % hand-over-hand locking in namei.
60 | % namei's use of refcount to prevent changing underfoot,
61 | % and lock when actually using it.
62 | % example of where deadlock is avoided? namei?
63 | % spawn a thread to evade a lock order problem?
64 |
65 | \section{ロック的なパターン}
66 |
67 | 多くの場面で,xv6 は参照カウントまたはフラグをソフトロック的なものとし
68 | て利用することで,そのオブジェクトがアロケート済みで,解放したり再利用
69 | してはならないことを表します.プロセスの \texttt{p->state} は,ファイル,
70 | \texttt{inode}, および \texttt{buf} 構造体の参照カウントと同様に動きま
71 | す.いずれの場合でも,ロックが存在してフラグ・参照カウントを保護しますが,
72 | オブジェクトの予期せぬ解放を防ぐのは後者です.
73 |
74 | ファイルシステムは \texttt{inode} 構造体の参照カウントを複数プロセスが
75 | 共有するロックのようなものとして使うことで,通常のロックならば生じてし
76 | まうデッドロックを回避します.たとえば,\texttt{namex}
77 | \lineref{kernel/fs.c:/^namex/} のループは,パス名に含まれる各部位に対応する名前のディ
78 | レクトリをロックします.しかし \texttt{namex} は,各ループの最後でそれ
79 | ぞれのロックを解放しなくてはなりません.複数のロックを持つと,ドットを
80 | 含むパス名(たとえば \texttt{a/./b})のときに自身とデッドロックするから
81 | です.そのディレクトリと \texttt{..} に並行アクセスするこ
82 | とによるデッドロックの可能性もあります.この問題は,\ref{CH:FS}~章で説明したように,
83 | ロックはせずに,しかし参照カウントは増やしたままで\texttt{inode} 構造体
84 | を次のループに持ち越すことで解決できます.
85 |
86 | その時々で異なるメカニズムで保護され,場合によっては明示的なロックでは
87 | なく,xv6 のコード構造による暗黙的な並行アクセスによって保護されるデー
88 | タもあります.たとえば,ある物理ページが使用可能なとき,それ
89 | は\texttt{kmem.lock} \lineref{kernel/kalloc.c:/^. kmem;/} によって保護されます.もしそ
90 | のページがそのあとパイプとしてアロケートされたら \lineref{kernel/pipe.c:/^pipealloc/}, 今度
91 | は別のロック (埋め込まれた \texttt{pi->lock}) で保護されます.また,そ
92 | のページが新しいプロセスのユーザメモリとしてさらに再アロケートされたら,
93 | ロックによる保護は無くなります.そのかわり,「アロケータはそのページを
94 | (解放されるまで)別のプロセスに渡さない」という事実が,そのページを並
95 | 行アクセスから保護します.新しいプロセスのメモリの所有権は複雑です.ま
96 | ず,\texttt{fork} では親プロセスがアロケートと操作を行い,そのあとで子プ
97 | ロセスが利用します.いずれ子プロセスが終了したら,親プロセスが再びその
98 | メモリを所有して \texttt{kfree} に渡します.このことから学ぶべきこと
99 | が 2 つあります.第一に,ライフタイムのときどきにおいて,
100 | 異なる方法で並行処理から保護されるデータオブジェクトがある.第二
101 | に,明示的なロックではなく,その暗黙的な構造によって保護がなされ
102 | ることがある.
103 |
104 | 最後のロック的なものの例は,\texttt{mycpu()} \lineref{kernel/proc.c:/^myproc/} 呼び出
105 | しの周辺で割込を無効化することです.割込を無効化すると,
106 | 呼び出し元のコードは,コンテキストスイッチを引き起こすタイマ割込に対し
107 | てアトミックになります.そうすることで,プロセスを別 CPU に移すことができる
108 | ようになります.
109 |
110 | \section{ロックしない}
111 |
112 | xv6 は,まったくロックを使わずに,書き換え可能なデータを共有する
113 | 箇所も少しあります.一例はスピンロックの実装ですが,RISC-V のアトミック
114 | 命令を,ハードウェア実装されたロックだと解釈することもできま
115 | す.別の例
116 | は \texttt{main.c} の \texttt{started} 変数
117 | \lineref{kernel/main.c:/^volatile/} で,0 番
118 | CPU が xv6 の初期を終えるまで,他の CPU が走るのを防ぐためのものです.
119 | ここでは,\texttt{volatile} を使用することで,コンパイラがたしかにロード
120 | 命令とストア命令を生成することを保証しています.最後の例
121 | は\texttt{proc.c} において \texttt{p->parent} を利用する箇所のいくつか
122 | です \lineref{kernel/proc.c:/^wait/}
123 | \lineref{kernel/proc.c:/^reparent/}.そこで正しくロックを行うと
124 | デッドロックを引き起こす可能性があり,かつ他のプロセスが同時
125 | に \texttt{p->parent} を書き換えることができないのはほとんど明らかです.
126 | 第四の例は \indexcode{p->killed} で,\texttt{p->lock} を保持する間にセット
127 | されますが \lineref{kernel/proc.c:/^kill/},ロックを取得することなくチェックされま
128 | す \lineref{kernel/trap.c:/p->killed/}.
129 |
130 | ある 1 つの CPU またはスレッドがデータを書き,また別の CPU またはスレッ
131 | ドが読むが,そのデータのための専用のロックが無いという箇所が xv6 にはあ
132 | ります.一例は \texttt{fork} で,親プロセスが子プロセスのユーザメモリペー
133 | ジに書き込みを行い,子プロセス(別のスレッド,場合によっては別の CPU)
134 | がそれを読みますが,それらのページを明示的に保護するロックはありません.
135 | 親プロセスが書き終えるまで子プロセスは実行を開始しませんので,厳密に言
136 | えば,これはロックが必要な箇所ではありません.しかし,メモリバリアが無い限り,
137 | ある CPU による書き込みが別 CPU に伝わる保証はないので,
138 | 潜在的なメモリの順序問題(\ref{CH:LOCK}~章を見てください)があります.しかし,親プロ
139 | セスがロックを解放するとともに,子プロセスは開始時にロックを取得するの
140 | で,\texttt{acquire} と \texttt{release} にあるメモリバリアが,親プ
141 | ロセスが書いた内容を子プロセスが見れることを保証します.
142 |
143 | \section{並列性}
144 |
145 | ロックとは,突き詰めれば,正しさのために並列性を抑制することです.パ
146 | フォーマンスもまた重要なので,カーネル設計者は,正しさと並列性を両立す
147 | るためのロックの使い方についてよく考えます.xv6 は性能のためにシステマ
148 | ティックに設計されたわけではありませんが,xv6 のオペレーションのうち並
149 | 列処理できるものと,ロックで競合するものについて考えることには価値があります.
150 |
151 | xv6 のパイプは,極めてよい並列性を持つものの例です.各パイプには固有の
152 | ロックがあるので,異なるプロセスは(異なる CPU で),異なるパイプを並列
153 | で読み書きできます.ただし,同じパイプを読み書きするプロセス同士は,ロッ
154 | クの解放をお互いに待たなくてはなりません.すなわち,同一のパイプに対す
155 | る同時読み書きはできません.空のパイプからの読み出し(または満杯のパイ
156 | プへの書き込み)についても同様ですが,これはロックに関連することではあ
157 | りません.
158 |
159 | コンテキストスイッチはさらに複雑な例です.それぞれ別の CPU で実行してい
160 | る2つのカーネルスレッドは,\texttt{yield}, \texttt{sched}, およ
161 | び\texttt{swtch} を同時に呼び,並列で実行することがありえます.
162 | どちらのスレッドもロックを取得しますが,それらは別のロックなので,
163 | お互いを待つことをしません.ただしスケジューラ内では,テーブルか
164 | ら \texttt{RUNNABLE} なプロセスを探すループにおいて,2つの CPU はコンフ
165 | リクトするかもしれません.すなわち,xv6 は,コンテキストスイッチ時
166 | に CPU が複数あることのご利益をうけることができますが,おそらく改善の余地
167 | があります.
168 |
169 | 別の例は,別の CPU で実行する別のプロセスが,並行して\texttt{fork} を呼
170 | び出した場合です.それらは,\texttt{pid\_lock}, \texttt{kmem.lock}, お
171 | よびプロセステーブルから \texttt{UNUSED} プロセスを探すためのプロセス固
172 | 有のロックのためにお互いを待つ可能性があります.一
173 | 方,2つの \texttt{fork} は,ユーザページのコピーとページテーブル用ペー
174 | ジの初期化を完全に並列で処理できます.
175 |
176 | 上記の例で示したロック方式は,並列性を犠牲にする場合があります.いずれ
177 | のケースでも,より練られた設計をすることで,並列度を改善することができ
178 | ます.やる価値があるかどうかは,その詳細によります.すなわち,そ
179 | のオペレーションが呼ばれる頻度,コードがロックの取り合いに費やす時間,
180 | それらのコードに別のボトルネックは無いか,などです.あるロック方式がパ
181 | フォーマンスの問題を生じているかどうか,また新しい方式により大きな改善
182 | を得られるかどうかを推定するのは難しいことがあります.よって,現実的な
183 | 負荷での計測が必要になることがしばしばあります.
184 |
185 | \section{練習問題}
186 |
187 | \begin{enumerate}
188 |
189 | \item xv6 のパイプ実装を改造して,異なるコアから同一パイプへの読み込
190 | みと書き出しが,同時に実行できるようにしてください.
191 |
192 | \item xv6 の \texttt{scheduler()} を改造して,異なるコアが同時に実行可
193 | 能プロセスを探すときに生じるロックの取り合いを減らしてください.
194 |
195 | \item xv6 の \texttt{fork()} におけるシリアル化をいくつか減らしてください.
196 |
197 | \end{enumerate}
198 |
199 | % is p->killed another example of cutting locking corners?
200 |
--------------------------------------------------------------------------------
/fig/mkernel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
220 |
--------------------------------------------------------------------------------
/tr2tex:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import re
5 | import sys
6 |
7 | def end(l):
8 | # look for " .", etc.
9 | p = re.compile(r'\s+([\.,:;])')
10 | l = p.sub(r'\1', l)
11 | # look for ) (
12 | p = re.compile(r'\s+\)([\.,]*) \(\n')
13 | m = p.search(l)
14 | if m != None:
15 | l = p.sub(r'', l)
16 | if len(m.groups()) > 0:
17 | l = "(" + l + ")%s\n" % m.groups()[0]
18 | else:
19 | l = "(" + l + ")\n"
20 | # look for '' ``
21 | p = re.compile(r"\s+''([\.,]*) ``\n")
22 | m = p.search(l)
23 | if m != None:
24 | l = p.sub(r'', l)
25 | if len(m.groups()) > 0:
26 | l = "``" + l + "''%s\n" % m.groups()[0]
27 | else:
28 | l = "``" + l + "''\n"
29 | # check for " )"
30 | p = re.compile(r'\s+([\)])')
31 | l = p.sub(r'\1', l)
32 | return l
33 |
34 | def ig2dotdot():
35 | line = f.readline()
36 | while line:
37 | if line.startswith('..'):
38 | return
39 | print("%", line, end="")
40 | line = f.readline()
41 |
42 | def chapter(l):
43 | p = re.compile(r'.chapter ([:\w]+) "(.+)"')
44 | l = p.sub(r'\\chapter{\2}\n\\label{\1}', l)
45 | print(l, end="")
46 |
47 | def chapter1(l):
48 | p = re.compile(r'.chapterlike "(.+)"')
49 | l = p.sub(r'\\chapter*{\1}\n', l)
50 | print(l, end="")
51 |
52 | def comment(l):
53 | p = re.compile(r'\.\\"(.*)')
54 | l = p.sub(r'%% \1', l)
55 | print(l, end="")
56 |
57 | def index(l):
58 | p = re.compile(r'.index ([-\w]+)')
59 | l = p.sub(r'\\index{\1}', l)
60 | p = re.compile(r'.index "(.*)"')
61 | l = p.sub(r'\\index{\1}', l)
62 | l = end(l)
63 | print(l, end="")
64 |
65 | def defindex(l):
66 | p = re.compile(r'.italic-index ([-\w]+)')
67 | l = p.sub(r'\\textit{\1}\\index{\1}', l)
68 | p = re.compile(r'.italic-index "(.*)"')
69 | l = p.sub(r'\\textit{\1}\\index{\1}', l)
70 | l = end(l)
71 | print(l, end="")
72 |
73 | codep = r'([\/\.\->_\w\&<\(\)\#\[\]\|=\*%\!]+)'
74 |
75 | def code(l):
76 | p = re.compile(r'.code "(.*)"')
77 | l = p.sub(r'\\lstinline{\1}', l)
78 | p = re.compile(r'.code ' + codep)
79 | l = p.sub(r'\\lstinline{\1}', l)
80 | l = end(l)
81 | print(l, end="")
82 |
83 | def codeindex(l):
84 | p = re.compile(r'.code-index ' + codep)
85 | l = p.sub(r'\\lstinline{\1}\\index{\1@\\lstinline{\1}}', l)
86 | p = re.compile(r'.code-index "(.*)"')
87 | l = p.sub(r'\\lstinline{\1}\\index{\1@\\lstinline{\1}}', l)
88 | l = end(l)
89 | print(l, end="")
90 |
91 | def section(l):
92 | p = re.compile(r'.section "(.+)"')
93 | l = p.sub(r'\\section{\1}', l)
94 | print(l, end="")
95 |
96 | def figref(l):
97 | p = re.compile(r'.figref ([:\w]+)')
98 | m = p.search(l)
99 | if p != None:
100 | n = m.groups()[0].split(':')
101 | if len(n) > 1:
102 | n = n[1]
103 | else:
104 | n = n[0]
105 | l = p.sub(r'Figure~\\ref{fig:%s}' % n, l)
106 | l = end(l)
107 | print(l, end="")
108 |
109 | def caption(fname):
110 | with open(fname) as f:
111 | line = f.readline()
112 | while line:
113 | if not line.startswith("."):
114 | return line.rstrip('\n')
115 | line = f.readline()
116 |
117 | def istable(fname):
118 | with open(fname) as f:
119 | line = f.readline()
120 | while line:
121 | if line.startswith(".TS"):
122 | return True
123 | line = f.readline()
124 | return False
125 |
126 | def table(fname):
127 | try:
128 | with open(fname) as f:
129 | print(r'\begin{tabular}{ll}')
130 | line = f.readline() # F1
131 | line = f.readline() # .TS
132 | line = f.readline() # center
133 | line = f.readline() # lb lb
134 | line = f.readline() # l l.
135 | line = f.readline() # heading
136 | p = re.compile(r'([\w\ ]+)\t+([\w]+)\n')
137 | line = p.sub(r'{\\bf \1} & {\\bf \2} \\\\', line)
138 | print(line)
139 | print("\midrule")
140 | # table
141 | line = f.readline()
142 | while line:
143 | if line.startswith(".TE"):
144 | break
145 | p = re.compile(r'([\(\)\w]+)\t+(.+)\n')
146 | line = p.sub(r'\1 & \2 \\\\', line)
147 | print(line)
148 | line = f.readline()
149 | line = f.readline() # F2
150 | line = f.readline() # caption
151 | print("\end{tabular}")
152 | print("\caption{" + line.strip("\n") + "}")
153 | except IOError:
154 | print("error: couldn't open %s" % fname, file=sys.stderr)
155 |
156 | def insert(l):
157 | l = l.rstrip('\n')
158 | fname = l.split(' ')[1]
159 | try:
160 | with open(fname) as f:
161 | line = f.readline()
162 | while line:
163 | print(line, end="")
164 | line = f.readline()
165 | except IOError:
166 | print("error: couldn't open %s" % fname, file=sys.stderr)
167 |
168 | def listing(l):
169 | print(r'\begin{lstlisting}[]')
170 | line = f.readline()
171 | while line:
172 | if line.startswith(".so"):
173 | insert(line)
174 | elif line.startswith(".P2"):
175 | break
176 | else:
177 | print(line, end="")
178 | line = f.readline()
179 | print("\end{lstlisting}");
180 |
181 | def figure(l):
182 | p = re.compile(r'.figure (.+)\n')
183 | l = p.sub(r'\1', l)
184 | name = "fig/%s.t" % l
185 | print("")
186 | print(r'\begin{figure}[t]')
187 | print("\center")
188 | if istable(name):
189 | table(name)
190 | else:
191 | print("\includegraphics[scale=0.5]{fig/%s.eps}" % l)
192 | print("\caption{%s}" % caption(name))
193 | print("\label{fig:%s}" % l)
194 | print("\end{figure}")
195 |
196 | def italic(l):
197 | p = re.compile(r'.italic ([\w]+)')
198 | l = p.sub(r'\\textit{\1}', l)
199 | p = re.compile(r'.italic "(.*)"')
200 | l = p.sub(r'\\textit{\1}', l)
201 |
202 | l = end(l)
203 | print(l, end="")
204 |
205 | def address(l):
206 | p = re.compile(r'.address (\w+)')
207 | l = p.sub(r'\\texttt{\1}', l)
208 | l = end(l)
209 | print(l, end="")
210 |
211 | def register(l):
212 | p = re.compile(r'.register (\w+)')
213 | l = p.sub(r'\\texttt{\%\1}', l)
214 | l = end(l)
215 | print(l, end="")
216 |
217 | def lineref(l):
218 | l = l.replace('!', '\\')
219 | p = re.compile(r'.line (.*):\/(.*)\/([+-]?\d+)?')
220 | l = p.sub(r'\\lineref{\1:/\2/\3}', l)
221 | p = re.compile(r'.line (.*):(\d+)')
222 | l = p.sub(r'\\lineref{\1:\2}', l)
223 | l = end(l)
224 | print(l, end="")
225 |
226 | def linerefs(l):
227 | l = l.replace('!', '\\')
228 | p = re.compile(r'.lines (.*):\/(.*)\/([+-]?\d+)?,\/(.*)\/([+-]?\d+)?')
229 | l = p.sub(r'\\linerefs{\1:/\2/\3,/\4/\5}', l)
230 | l = end(l)
231 | print(l, end="")
232 |
233 | def indent(l):
234 | p = re.compile(r'.IP (.*)\n')
235 | l = p.sub(r'\\paragraph{\\textbullet}', l)
236 | l = end(l)
237 | print(l, end="")
238 |
239 | def file(l):
240 | p = re.compile(r'.file ([-\w\/\.]+)')
241 | l = p.sub(r'\\fileref{\1}', l)
242 | p = re.compile(r'.file "(.*)"')
243 | l = p.sub(r'\\fileref{\1}', l)
244 | l = end(l)
245 | print(l, end="")
246 |
247 | def doref(l):
248 | # replace references in a line
249 | p = re.compile(r' \\\*\[([\w:]*)\]')
250 | l = p.sub(r'~\\ref{\1}', l)
251 | return l
252 |
253 | def doquote(l):
254 | # replace references in a line
255 | p = re.compile(r'"([\w]+)"')
256 | l = p.sub(r"``\1''", l)
257 | return l
258 |
259 | def docarrot(l):
260 | p = re.compile(r'(\d+)\^(\d+)(-\d+)*')
261 | l = p.sub(r'$\1^{\2}\3$', l)
262 | return l
263 |
264 | def dourl(l):
265 | p = re.compile(r'https://([\w\.\/]+)')
266 | l = p.sub(r'\\url{https://\1}', l)
267 | return l
268 |
269 | def tr2tex(l):
270 | if l.startswith(". "):
271 | l = "." + l[2:]
272 |
273 | if l.startswith(".ig"):
274 | ig2dotdot()
275 | elif l.startswith(".chapter "):
276 | chapter(l)
277 | elif l.startswith(".chapterlike"):
278 | chapter1(l)
279 | elif l.startswith(".PP"):
280 | print("")
281 | elif l.startswith(".code "):
282 | code(l)
283 | elif l.startswith(".italic-index"):
284 | defindex(l)
285 | elif l.startswith(".index"):
286 | index(l)
287 | elif l.startswith(".code-index"):
288 | codeindex(l)
289 | elif l.startswith(".section"):
290 | section(l)
291 | elif l.startswith(r'.\"'):
292 | comment(l)
293 | elif l.startswith(r'.figref'):
294 | figref(l)
295 | elif l.startswith(r'.figure '):
296 | figure(l)
297 | elif l.startswith(r'.italic'):
298 | italic(l)
299 | elif l.startswith(r'.address '):
300 | address(l)
301 | elif l.startswith(r'.register '):
302 | register(l)
303 | elif l.startswith(r'.line '):
304 | lineref(l)
305 | elif l.startswith(r'.lines '):
306 | linerefs(l)
307 | elif l.startswith(r'.P1'):
308 | listing(l)
309 | elif l.startswith(r'.IP'):
310 | indent(l)
311 | elif l.startswith(r'.file'):
312 | file(l)
313 | elif l.startswith(r'.sheet'):
314 | return
315 | elif l.startswith(r"."):
316 | print("TODO", l)
317 | else:
318 | l = docarrot(l)
319 | l = doref(l)
320 | l = doquote(l)
321 | l = dourl(l)
322 | print(l, end="")
323 |
324 | with open(sys.argv[1]) as f:
325 | line = f.readline()
326 | while line:
327 | tr2tex(line)
328 | line = f.readline()
329 |
--------------------------------------------------------------------------------
/fig/os.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
285 |
--------------------------------------------------------------------------------
/book.mac:
--------------------------------------------------------------------------------
1 | .\"
2 | .\" preliminary formatting, subject to change.
3 | .\" mostly lifted from rsc's thesis and cleaned up.
4 | .\"
5 | .do xflag 3 \" heirloom troff: enable long names like groff
6 | .\" all warnings
7 | .warn +w
8 | .mediasize letter
9 | .hylang en_US
10 | .lc_ctype en_US.UTF-8
11 | .nr realnumbers 1
12 | .if \n[realnumbers] .pn \np
13 | .\"
14 | .\" fonts
15 | .\"
16 | .fp 1 R MinionPro-Regular otf
17 | .feature R +tnum
18 | .fp 2 I MinionPro-It otf
19 | .fp 3 B MinionPro-Bold otf
20 | .fp 4 BI MinionPro-BoldIt otf
21 | .fp 5 C LucidaSans-Typewriter83 pfa
22 | .\" Use vertically centered tilde, circumflex
23 | .ftr R ~\[asciitilde]
24 | .ftr I ~\[asciitilde]
25 | .ftr B ~\[asciitilde]
26 | .ftr BI ~\[asciitilde]
27 | .ftr C ~\[asciitilde]
28 | .ftr R ^\[asciicircum]
29 | .ftr I ^\[asciicircum]
30 | .ftr B ^\[asciicircum]
31 | .ftr BI ^\[asciicircum]
32 | .ftr C ^\[asciicircum]
33 | .\"
34 | .\" date
35 | .\"
36 | .\"9 .fp 5 C LucidaSansCW83
37 | .if \n(mo==1 .ds mo January
38 | .if \n(mo==2 .ds mo February
39 | .if \n(mo==3 .ds mo March
40 | .if \n(mo==4 .ds mo April
41 | .if \n(mo==5 .ds mo May
42 | .if \n(mo==6 .ds mo June
43 | .if \n(mo==7 .ds mo July
44 | .if \n(mo==8 .ds mo August
45 | .if \n(mo==9 .ds mo September
46 | .if \n(mo==10 .ds mo October
47 | .if \n(mo==11 .ds mo November
48 | .if \n(mo==12 .ds mo December
49 | .nr _ (\n(yr%100)+2000
50 | .ds yr \n_
51 | .ds date "\*(mo \n(dy, \*(yr
52 | .\"
53 | .\" initialization
54 | .\"
55 | .ds center-footer "\\n%
56 | .ds center-header "
57 | .ds left-footer "\s-2DRAFT as of \\*[date]\s+2
58 | .ds left-header "
59 | .ds right-footer "\s-2https://pdos.csail.mit.edu/6.828/xv6\s+2
60 | .ds right-header "
61 | .nr IL 0
62 | .nr ID 4n
63 | .nr IN 0
64 | .nr h1 0 1
65 | .nr line-length 6i
66 | .nr page-line-length 0
67 | .nr left-margin 1.25i
68 | .nr P0 0.4v
69 | .nr page-length 11i
70 | .nr footer-margin 1i
71 | .nr header-margin 1i
72 | .nr point-size 12
73 | .nr RN 0 1
74 | .nr T0 4n
75 | .nr exercise 0 1
76 | .ft 1
77 | .\"
78 | .\" wa - print a warning
79 | .\"
80 | .de wa
81 | .tm \\n(.F:\\n(.c: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
82 | ..
83 | .\"
84 | .\" init - run-time initialization
85 | .\"
86 | .de notrap
87 | .wh 0
88 | .wh -\\n[footer-margin]u
89 | .wh -\\n[footer-margin]u/2u
90 | ..
91 | .de trap
92 | .wh 0 new-page
93 | .wh -\\n[footer-margin]u page-footer
94 | .wh -\\n[footer-margin]u/2u page-bottom
95 | ..
96 | .de init
97 | .ll \\n[line-length]u
98 | .po \\n[left-margin]u
99 | .pl \\n[page-length]u
100 | .new-page
101 | .trap
102 | .nr page-number 1
103 | ..
104 | .\"
105 | .\" new page
106 | .\"
107 | .de new-page
108 | .sp \\n[header-margin]u/2u
109 | .ev 1
110 | .in 0i
111 | .ll \\n[line-length]u
112 | .ad c
113 | 'tl '\\*[left-header]'\\*[center-header]'\\*[right-header]'
114 | .nr top \\n[header-margin]
115 | \\X'top \\n[top]'
116 | .if \\n[have-postponed-figs] \{ .\"
117 | .br
118 | .ad l
119 | .sp |\\n[top]u
120 | .postponed-figs
121 | .mk top
122 | .ds postponed-figs "
123 | .nr have-postponed-figs 0
124 | .\}
125 | .ev
126 | 'sp |\\n[top]u
127 | ..
128 | .\"
129 | .\" page footer
130 | .\"
131 | .de page-footer
132 | .nr column +1
133 | .ie \\n[column]<\\n[columns] .next-column
134 | .el \{\
135 | .ll \\n[line-length]u
136 | .po -((\\n[columns]u-1u)*(\\n[line-length]u+\\n[column-margin]u))
137 | .nr saved-po \\n(.o
138 | .ch page-footer 20i
139 | .ev 1
140 | 'sp |(\\n[page-length]u-\\n[footer-margin]u)
141 | .bp
142 | .ev
143 | .po \\n[saved-po]u
144 | .ch page-footer -\\n[footer-margin]u
145 | .nr column 0
146 | .\}
147 | ..
148 | .\"
149 | .\" .multi-column n margin
150 | .\"
151 | .de multi-column
152 | .nr columns \\$1
153 | .nr column-margin \\$2
154 | .nr page-line-length \\n[line-length]u
155 | .nr line-length (\\n[line-length]u-((\\n[columns]-1)*\\n[column-margin]u))/\\n[columns]
156 | ..
157 | .nr columns 1
158 | .nr column-margin 0
159 | .de next-column
160 | 'sp |\\n[top]u
161 | .po +\\n[line-length]u
162 | .po +\\n[column-margin]u
163 | ..
164 | .\"
165 | .\" page bottom
166 | .\"
167 | .de page-bottom
168 | .nr save-ps \\n(.s
169 | .nr save-ft \\n(.f
170 | .ft 1
171 | .ps \\n[point-size]
172 | .ie \\n[page-line-length]>0 'lt \\n[page-line-length]u
173 | .el 'lt \\n[line-length]u
174 | .po \\n[left-margin]u
175 | .if \\n%>0 .tl \(ts\\*[left-footer]\(ts\\*[center-footer]\(ts\\*[right-footer]\(ts
176 | .\" show index terms in upper right margin
177 | .ch page-bottom 20i
178 | .index-trap
179 | .ft \\n[save-ft]
180 | .ps \\n[save-ps]
181 | .sp |\\nxu
182 | .ch page-bottom -\\n[footer-margin]u/2u
183 | ..
184 | .de index-trap
185 | .ev 1
186 | .mk x
187 | .sp |\\n[top]u
188 | .ad l
189 | .ps 9
190 | .vs 10.8
191 | .nr marginalia \\n[left-margin]u+\\n[line-length]u+0.2i
192 | .po \\n[marginalia]u
193 | .ll (8.4i-\\n[marginalia]u)
194 | .index-terms
195 | .br
196 | .ds index-terms "
197 | .po \\n[left-margin]u
198 | .ev
199 | ..
200 | .de index-terms
201 | ..
202 | .\"
203 | .\" insert index term
204 | .\" (no display, just creates the entry)
205 | .\"
206 | .de index
207 | .if !'\\$2'' .wa "likely missing quote in .index argument: \\$1 \\$2 \\$3 \\$4 \\$5
208 | .am index-terms xx
209 | .if \\n[index-margins] \\h'-0.1i'\\$1\c
210 | \\X'index \\$1'
211 | .br
212 | .xx
213 | ..
214 | .\"
215 | .\" bold
216 | .\"
217 | .de bold
218 | .font-change B "\\$1" "\\$2\\&" "\\$3"
219 | ..
220 | .\"
221 | .\" code font
222 | .\"
223 | .de code
224 | .font-change C "\\$1" "\\$2\\&" "\\$3"
225 | ..
226 | .de code-index
227 | .font-change C "\\$1" "\\$2\&" "\\$3"
228 | .index "\\$1+code
229 | ..
230 | .\"
231 | .\" font change
232 | .\"
233 | .de font-change
234 | .ie \\n(.$==1 .ft \\$1
235 | .el \&\\$4\\f\\$1\\$2\&\\fP\\$3
236 | ..
237 | .\"
238 | .\" italic
239 | .\"
240 | .de italic
241 | .font-change I "\\$1" "\\$2\\&" "\\$3"
242 | ..
243 | .de italic-index
244 | .font-change I "\\$1" "\\$2\&" "\\$3"
245 | .index "\\$1
246 | ..
247 | .\"
248 | .\" left-adjust paragraph
249 | .\"
250 | .nr indent 0
251 | .nr para-spacing 0
252 | .de LP
253 | .in \\n[indent]u
254 | .br
255 | .hy 1
256 | .ad b
257 | .fi
258 | .ft R
259 | .sp \\n[para-spacing]u
260 | .PS
261 | ..
262 | .\"
263 | .\" ordinary paragraph
264 | .\"
265 | .nr LP 0
266 | .de PP
267 | .if \\n(LP>0 .sp 0.2v
268 | .LP
269 | .if \\n(LP>0 .nr LP 0
270 | .ti 2m
271 | ..
272 | .\"
273 | .\" indented paragraph
274 | .\"
275 | .de IP
276 | .br
277 | .ne 3
278 | .sp 0.2v
279 | .ie '\\$2'' .nr IP 4n
280 | .el .nr IP \\$2
281 | .in \\n(INu+\\n(IPu
282 | .if !'\\$1'' \{\
283 | . ti -\\n(IPu
284 | \&\\$1
285 | . ie \\n(.k>=\\n(.i .br
286 | . el .sp -1\}
287 | ..
288 | .\"
289 | .\" set point size
290 | .\"
291 | .de PS
292 | .ps \\n[point-size]
293 | .nr _ \\n[point-size]*1200u
294 | .vs \\n_u
295 | ..
296 | .\"
297 | .\" chapter-like
298 | .\"
299 | .de big-heading
300 | .init
301 | .mk x
302 | .if !'\\$2'' \v'-1v-4p'\\X'PDFMark: Bookmark 0 \\$2'
303 | .if !'\\$3'' \A'toc-\\$3'
304 | .sp |\\nxu
305 | .ft B
306 | .ps 20
307 | .vs 24
308 | .if !'\\$1'' \\$1
309 | ..
310 | .\"
311 | .\" chapter-like heading
312 | .\"
313 | .de chapterlike
314 | .big-heading "" "\\$1" "\\$1
315 | \\$1
316 | .sp
317 | .LP
318 | ..
319 | .\"
320 | .\" chapter heading
321 | .\"
322 | .de chapter
323 | .big-heading "Chapter \\*[\\$1]" "\\*[\\$1]: \\$2" "\\*[\\$1]
324 | .sp
325 | \\$2
326 | .sp
327 | .LP
328 | ..
329 | .\"
330 | .\" Appendix heading
331 | .\"
332 | .de appendix
333 | .big-heading "Appendix \\*[\\$1]" "\\*[\\$1]: \\$2" "\\*[\\$1]
334 | .sp
335 | \\$2
336 | .sp
337 | .LP
338 | ..
339 | .\"
340 | .\" section heading
341 | .\"
342 | .de section
343 | .br
344 | .ft B
345 | .ps 16
346 | .vs 18
347 | .sp 1.5
348 | \\$1\v'-1v'\\X'PDFMark: Bookmark 1 \\$1'
349 | .PS
350 | .sp 0.5
351 | .ft R
352 | .LP
353 | ..
354 | .\"
355 | .\" exercises
356 | .\"
357 | .de exercise
358 | .LP
359 | \\n+[exercise].
360 | ..
361 | .de xx
362 | ..
363 | .de answer xx
364 | .ig
365 | .xx
366 | .\"
367 | .\" font change aliases
368 | .\"
369 | .de register-font
370 | .code "\\$1" "\\$2" "\\$3"
371 | ..
372 | .de register
373 | .register-font "%\\$1" "\\$2" "\\$3"
374 | ..
375 | .de opcode
376 | .code "\\$1" "\\$2" "\\$3"
377 | ..
378 | .de file
379 | .code "\\$1" "\\$2" "\\$3"
380 | ..
381 | .de address
382 | .code "\\$1" "\\$2" "\\$3"
383 | ..
384 | .de smallcaps
385 | \&\\$3\s-3\\$1\s+3\\$2
386 | ..
387 | .de sheet
388 | .line "\\$1:1" "\\$2" "\\$3"
389 | ..
390 | .de line
391 | .ds LINE xxxx
392 | .sy touch .turd
393 | .sy ./line \\$1 >.turd
394 | .so .turd
395 | .sy rm -f .turd
396 | .if 'xxxx'\\*[LINE]' .tm LINE FAIL: "\\$1"
397 | \&\\$3\s-3(\\*[LINE])\s+3\\$2
398 | ..
399 | .de lines
400 | .line "\\$1" "\\$2" "\\$3"
401 | ..
402 | .\"
403 | .\" P1 - code start
404 | .\"
405 | .de P1
406 | .sp 0.3v
407 | .IP \\$1
408 | .nf
409 | .ft C
410 | .ta +4n +4n +4n +4n +4n +4n +4n +4n
411 | .ps 10
412 | .vs 12
413 | ..
414 | .\"
415 | .\" P2 - code end
416 | .\"
417 | .de P2
418 | .sp 0.4v
419 | .PS
420 | .LP
421 | ..
422 | .\"
423 | .\" P3 - code break
424 | .\"
425 | .de P3
426 | .P2
427 | .P1
428 | ..
429 | .\"
430 | .\" TS/TE - no-ops for tbl
431 | .\"
432 | .de TS
433 | ..
434 | .de TE
435 | ..
436 | .\" F1/F2/F3 - Figures
437 | .\"
438 | .\" .F1
439 | .\" Figure
440 | .\" .F2
441 | .\" Caption
442 | .\" .F3
443 | .\"
444 | .de F1
445 | .nr top 0
446 | .ll \\n[line-length]u
447 | .po \\n[left-margin]u
448 | .pl \\n[page-length]u
449 | .sp |0i
450 | .mk x
451 | .ds Fignum \\*[fig:\\*[the-figure]]
452 | \\X'start \\nx'\A'fig-\\*[Fignum]'
453 | ..
454 | .de F2
455 | .LP
456 | .ps -2
457 | .vs -2
458 | .br
459 | \fBFigure \\*[Fignum]\fP.
460 | ..
461 | .de F3
462 | .br
463 | .ps +2
464 | .vs +2
465 | \D'l \n[line-length]u 0'
466 | .sp 0.125i
467 | .mk x
468 | .index-trap
469 | \\X'end \\nx'
470 | ..
471 | .\"
472 | .\" figure - load figure from file
473 | .\"
474 | .\" .figure name file
475 | .\"
476 | .nr have-postponed-figs 0
477 | .de postponed-figs
478 | ..
479 | .de figure
480 | .br
481 | .if '\\$1'' .wa .figure needs name argument
482 | .if !'\\$2'' .wa .figure file is implied by name
483 | .\" flush postponed figures
484 | .\" to avoid reordering: want Figure 2 after Figure 1.
485 | .if \\n[have-postponed-figs] .bp
486 | .ds Fignum \\*[fig:\\$1]
487 | .nr Fight \\n[fig-\\$1-height]
488 | .ds Figfile \\*[fig-\\$1-file]
489 | .ie \\n[Fight]-\\n(.t \{ .\"
490 | .\" postpone figure to next page.
491 | .\".wa "Figure \\*[Fignum] postponed \\n[Fight] from \\*[Figfile]
492 | .nr have-postponed-figs 1
493 | .am postponed-figs
494 | \\X'figure \\*[Figfile] \\n[Fight]'
495 | .sp \\n[Fight]u
496 | \\..
497 | .\}
498 | .el \{ .\"
499 | .\" put figure at top of this page.
500 | .\".wa "Figure \\*[Fignum] at top of this page from \\*[Figfile]
501 | \\X'top \\n[top]'
502 | \\X'figure \\*[Figfile] \\n[Fight]'
503 | .nr top +\\n[Fight]
504 | .sp \\n[Fight]u
505 | .\}
506 | ..
507 | .\"
508 | .\" figref - hyperlinked figure reference
509 | .\"
510 | .de figref
511 | \\$3\T'fig-\\*[fig:\\$1]'Figure \\*[fig:\\$1]\T\\$2
512 | ..
513 | .\"
514 | .\" EPS - Encapsulated PostScript
515 | .\"
516 | .\" .EPS file [scale]
517 | .\"
518 | .\" scale is a percentage (25 means 1/4)
519 | .\"
520 | .de EPS
521 | .psbb \\$1
522 | .nr eps_scale 100
523 | .if !'\\$2'' .nr eps_scale \\$2
524 | .nr y (\\n[ury]p-\\n[lly]p)*\\n[eps_scale]/100
525 | .nr x (\\n[urx]p-\\n[llx]p)*\\n[eps_scale]/100
526 | .nr z (\\n[line-length]u-\\nxu)/2
527 | .\" Troff sees the PI request as a character on a line.
528 | .\" Tell it the line height is the same height as the
529 | .\" EPS file, and then adjust the EPS file upward
530 | .\" to match.
531 | .PI \\$1 "\\nyu,\\nxu,0,\\nzu"
532 | .sp \\nyu
533 | .\" Reset line height.
534 | .PS
535 | ..
536 | .nr index-margins 1 \" 1 = show index terms in margins, 0 = don't
537 | .\"
538 | .\"
539 | .\" table of contents
540 | .\"
541 | .ds CH:UNIX 0
542 | .ds CH:FIRST 1
543 | .ds CH:MEM 2
544 | .ds CH:TRAP 3
545 | .ds CH:LOCK 4
546 | .ds CH:SCHED 5
547 | .ds CH:FS 6
548 | .ds CH:SUM 7
549 | .ds APP:HW A
550 | .ds APP:BOOT B
551 |
--------------------------------------------------------------------------------
/fig/race.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
260 |
--------------------------------------------------------------------------------
/fig/fslayer.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
275 |
--------------------------------------------------------------------------------
/font/LucidaSans-Typewriter83.afm:
--------------------------------------------------------------------------------
1 | StartFontMetrics 2.0
2 | Comment AFM Generated by Ghostscript/pf2afm
3 | FontName LucidaSans-Typewriter83
4 | FullName Lucida Sans Typewriter 83
5 | FamilyName LucidaSansTypewriter83
6 | Weight Medium
7 | Notice Copyright (c) 1991 Bigelow & Holmes Inc. and Y&Y, Inc. (508) 371-3286. All Rights Reserved.
8 | ItalicAngle 0
9 | IsFixedPitch true
10 | UnderlinePosition -100
11 | UnderlineThickness 50
12 | Version 1.003
13 | EncodingScheme FontSpecific
14 | FontBBox -12 -205 618 928
15 | StartCharMetrics 246
16 | C 32 ; WX 502 ; N space ; B 0 0 0 0 ;
17 | C 33 ; WX 502 ; N exclam ; B 201 0 301 603 ;
18 | C 34 ; WX 502 ; N quotedbl ; B 100 422 402 643 ;
19 | C 35 ; WX 502 ; N numbersign ; B 22 0 486 603 ;
20 | C 36 ; WX 502 ; N dollar ; B 88 -50 431 653 ;
21 | C 37 ; WX 502 ; N percent ; B 0 -15 502 618 ;
22 | C 38 ; WX 502 ; N ampersand ; B 9 -15 502 618 ;
23 | C 39 ; WX 502 ; N quoteright ; B 191 392 312 643 ;
24 | C 40 ; WX 502 ; N parenleft ; B 141 -121 442 643 ;
25 | C 41 ; WX 502 ; N parenright ; B 60 -121 362 643 ;
26 | C 42 ; WX 502 ; N asterisk ; B 67 252 435 603 ;
27 | C 43 ; WX 502 ; N plus ; B 35 0 467 432 ;
28 | C 44 ; WX 502 ; N comma ; B 191 -131 312 121 ;
29 | C 45 ; WX 502 ; N hyphen ; B 85 186 417 246 ;
30 | C 46 ; WX 502 ; N period ; B 191 0 312 121 ;
31 | C 47 ; WX 502 ; N slash ; B 30 -121 472 643 ;
32 | C 48 ; WX 502 ; N zero ; B 45 -15 457 618 ;
33 | C 49 ; WX 502 ; N one ; B 75 0 477 618 ;
34 | C 50 ; WX 502 ; N two ; B 68 0 413 618 ;
35 | C 51 ; WX 502 ; N three ; B 95 -15 428 618 ;
36 | C 52 ; WX 502 ; N four ; B 45 0 452 603 ;
37 | C 53 ; WX 502 ; N five ; B 110 -15 419 603 ;
38 | C 54 ; WX 502 ; N six ; B 66 -15 457 618 ;
39 | C 55 ; WX 502 ; N seven ; B 82 0 445 603 ;
40 | C 56 ; WX 502 ; N eight ; B 65 -15 448 618 ;
41 | C 57 ; WX 502 ; N nine ; B 61 -15 451 618 ;
42 | C 58 ; WX 502 ; N colon ; B 191 0 312 442 ;
43 | C 59 ; WX 502 ; N semicolon ; B 191 -131 312 442 ;
44 | C 60 ; WX 502 ; N less ; B 35 0 467 432 ;
45 | C 61 ; WX 502 ; N equal ; B 35 116 467 316 ;
46 | C 62 ; WX 502 ; N greater ; B 35 0 467 432 ;
47 | C 63 ; WX 502 ; N question ; B 67 0 442 618 ;
48 | C 64 ; WX 502 ; N at ; B 30 -15 502 618 ;
49 | C 65 ; WX 502 ; N A ; B 5 0 497 603 ;
50 | C 66 ; WX 502 ; N B ; B 75 0 453 603 ;
51 | C 67 ; WX 502 ; N C ; B 42 -15 470 618 ;
52 | C 68 ; WX 502 ; N D ; B 53 0 471 603 ;
53 | C 69 ; WX 502 ; N E ; B 88 0 459 603 ;
54 | C 70 ; WX 502 ; N F ; B 100 0 472 603 ;
55 | C 71 ; WX 502 ; N G ; B 31 -15 459 618 ;
56 | C 72 ; WX 502 ; N H ; B 60 0 442 603 ;
57 | C 73 ; WX 502 ; N I ; B 70 0 432 603 ;
58 | C 74 ; WX 502 ; N J ; B 80 -15 379 603 ;
59 | C 75 ; WX 502 ; N K ; B 69 0 489 603 ;
60 | C 76 ; WX 502 ; N L ; B 90 0 452 603 ;
61 | C 77 ; WX 502 ; N M ; B 38 0 463 603 ;
62 | C 78 ; WX 502 ; N N ; B 60 0 442 603 ;
63 | C 79 ; WX 502 ; N O ; B 25 -15 477 618 ;
64 | C 80 ; WX 502 ; N P ; B 93 0 471 603 ;
65 | C 81 ; WX 502 ; N Q ; B 25 -131 513 618 ;
66 | C 82 ; WX 502 ; N R ; B 75 0 489 603 ;
67 | C 83 ; WX 502 ; N S ; B 67 -15 454 618 ;
68 | C 84 ; WX 502 ; N T ; B 10 0 492 603 ;
69 | C 85 ; WX 502 ; N U ; B 63 -15 440 603 ;
70 | C 86 ; WX 502 ; N V ; B 6 0 496 603 ;
71 | C 87 ; WX 502 ; N W ; B 7 0 495 603 ;
72 | C 88 ; WX 502 ; N X ; B 4 0 496 603 ;
73 | C 89 ; WX 502 ; N Y ; B 13 0 499 603 ;
74 | C 90 ; WX 502 ; N Z ; B 40 0 462 603 ;
75 | C 91 ; WX 502 ; N bracketleft ; B 181 -121 432 643 ;
76 | C 92 ; WX 502 ; N backslash ; B 30 -121 472 643 ;
77 | C 93 ; WX 502 ; N bracketright ; B 70 -121 322 643 ;
78 | C 94 ; WX 502 ; N asciicircum ; B 35 121 467 603 ;
79 | C 95 ; WX 502 ; N underscore ; B 0 -60 502 0 ;
80 | C 96 ; WX 502 ; N quoteleft ; B 191 392 312 643 ;
81 | C 97 ; WX 502 ; N a ; B 55 -7 481 452 ;
82 | C 98 ; WX 502 ; N b ; B 70 -10 457 643 ;
83 | C 99 ; WX 502 ; N c ; B 67 -10 449 452 ;
84 | C 100 ; WX 502 ; N d ; B 52 -10 438 643 ;
85 | C 101 ; WX 502 ; N e ; B 58 -10 447 452 ;
86 | C 102 ; WX 502 ; N f ; B 60 0 488 653 ;
87 | C 103 ; WX 502 ; N g ; B 50 -171 439 452 ;
88 | C 104 ; WX 502 ; N h ; B 74 0 431 643 ;
89 | C 105 ; WX 502 ; N i ; B 70 0 322 643 ;
90 | C 106 ; WX 502 ; N j ; B 60 -171 364 643 ;
91 | C 107 ; WX 502 ; N k ; B 87 0 483 643 ;
92 | C 108 ; WX 502 ; N l ; B 70 0 322 643 ;
93 | C 109 ; WX 502 ; N m ; B 38 0 464 452 ;
94 | C 110 ; WX 502 ; N n ; B 74 0 431 452 ;
95 | C 111 ; WX 502 ; N o ; B 43 -10 459 452 ;
96 | C 112 ; WX 502 ; N p ; B 73 -161 459 452 ;
97 | C 113 ; WX 502 ; N q ; B 45 -161 432 452 ;
98 | C 114 ; WX 502 ; N r ; B 126 0 452 452 ;
99 | C 115 ; WX 502 ; N s ; B 76 -10 429 452 ;
100 | C 116 ; WX 502 ; N t ; B 53 -10 448 528 ;
101 | C 117 ; WX 502 ; N u ; B 72 -10 428 442 ;
102 | C 118 ; WX 502 ; N v ; B 25 0 477 442 ;
103 | C 119 ; WX 502 ; N w ; B 5 0 497 442 ;
104 | C 120 ; WX 502 ; N x ; B 38 0 469 442 ;
105 | C 121 ; WX 502 ; N y ; B 34 -161 479 442 ;
106 | C 122 ; WX 502 ; N z ; B 55 0 447 442 ;
107 | C 123 ; WX 502 ; N braceleft ; B 95 -121 417 643 ;
108 | C 124 ; WX 502 ; N bar ; B 221 -121 281 643 ;
109 | C 125 ; WX 502 ; N braceright ; B 85 -121 407 643 ;
110 | C 126 ; WX 502 ; N asciitilde ; B 35 142 467 290 ;
111 | C 161 ; WX 502 ; N exclamdown ; B 201 -161 301 442 ;
112 | C 162 ; WX 502 ; N cent ; B 80 0 413 603 ;
113 | C 163 ; WX 502 ; N sterling ; B 103 0 434 618 ;
114 | C 164 ; WX 502 ; N fraction ; B 0 -15 502 618 ;
115 | C 165 ; WX 502 ; N yen ; B 20 0 492 603 ;
116 | C 166 ; WX 502 ; N florin ; B 93 -121 466 618 ;
117 | C 167 ; WX 502 ; N section ; B 88 -133 418 618 ;
118 | C 168 ; WX 502 ; N currency ; B 45 96 457 508 ;
119 | C 169 ; WX 502 ; N quotesingle ; B 191 392 312 643 ;
120 | C 170 ; WX 502 ; N quotedblleft ; B 100 422 402 643 ;
121 | C 171 ; WX 502 ; N guillemotleft ; B 48 35 445 404 ;
122 | C 172 ; WX 502 ; N guilsinglleft ; B 142 35 361 404 ;
123 | C 173 ; WX 502 ; N guilsinglright ; B 142 35 361 404 ;
124 | C 174 ; WX 502 ; N fi ; B 50 0 427 653 ;
125 | C 175 ; WX 502 ; N fl ; B 50 0 427 653 ;
126 | C 177 ; WX 502 ; N endash ; B 55 221 447 271 ;
127 | C 178 ; WX 502 ; N dagger ; B 80 -121 422 603 ;
128 | C 179 ; WX 502 ; N daggerdbl ; B 80 -121 422 603 ;
129 | C 180 ; WX 502 ; N periodcentered ; B 201 171 301 271 ;
130 | C 182 ; WX 502 ; N paragraph ; B 51 -121 408 603 ;
131 | C 183 ; WX 502 ; N bullet ; B 131 118 372 359 ;
132 | C 184 ; WX 502 ; N quotesinglbase ; B 191 -131 312 121 ;
133 | C 185 ; WX 502 ; N quotedblbase ; B 100 -121 402 100 ;
134 | C 186 ; WX 502 ; N quotedblright ; B 100 422 402 643 ;
135 | C 187 ; WX 502 ; N guillemotright ; B 57 35 454 404 ;
136 | C 188 ; WX 502 ; N ellipsis ; B 43 0 458 80 ;
137 | C 189 ; WX 502 ; N perthousand ; B 0 -15 502 618 ;
138 | C 191 ; WX 502 ; N questiondown ; B 60 -171 435 442 ;
139 | C 193 ; WX 502 ; N grave ; B 171 522 352 643 ;
140 | C 194 ; WX 502 ; N acute ; B 151 522 332 643 ;
141 | C 195 ; WX 502 ; N circumflex ; B 116 522 387 643 ;
142 | C 196 ; WX 502 ; N tilde ; B 116 522 387 618 ;
143 | C 197 ; WX 502 ; N macron ; B 136 522 367 583 ;
144 | C 198 ; WX 502 ; N breve ; B 116 522 387 643 ;
145 | C 199 ; WX 502 ; N dotaccent ; B 211 522 291 603 ;
146 | C 200 ; WX 502 ; N dieresis ; B 136 522 367 593 ;
147 | C 202 ; WX 502 ; N ring ; B 180 522 327 669 ;
148 | C 203 ; WX 502 ; N cedilla ; B 202 -161 336 0 ;
149 | C 205 ; WX 502 ; N hungarumlaut ; B 123 522 399 643 ;
150 | C 206 ; WX 502 ; N ogonek ; B 172 -161 306 0 ;
151 | C 207 ; WX 502 ; N caron ; B 116 522 387 643 ;
152 | C 208 ; WX 502 ; N emdash ; B 25 221 477 271 ;
153 | C 225 ; WX 502 ; N AE ; B 0 0 487 603 ;
154 | C 227 ; WX 502 ; N ordfeminine ; B 97 291 421 618 ;
155 | C 232 ; WX 502 ; N Lslash ; B -10 0 452 603 ;
156 | C 233 ; WX 502 ; N Oslash ; B 25 -15 478 618 ;
157 | C 234 ; WX 502 ; N OE ; B 30 -15 495 618 ;
158 | C 235 ; WX 502 ; N ordmasculine ; B 90 291 412 618 ;
159 | C 241 ; WX 502 ; N ae ; B 20 -10 490 452 ;
160 | C 245 ; WX 502 ; N dotlessi ; B 70 0 311 442 ;
161 | C 248 ; WX 502 ; N lslash ; B 70 0 422 643 ;
162 | C 249 ; WX 502 ; N oslash ; B 43 -10 460 452 ;
163 | C 250 ; WX 502 ; N oe ; B 20 -10 492 452 ;
164 | C 251 ; WX 502 ; N germandbls ; B 78 -10 478 653 ;
165 | C -1 ; WX 502 ; N threesuperior ; B 128 291 388 618 ;
166 | C -1 ; WX 502 ; N Aring ; B 5 0 497 737 ;
167 | C -1 ; WX 502 ; N Ntilde ; B 60 0 442 748 ;
168 | C -1 ; WX 502 ; N Yacute ; B 13 0 499 773 ;
169 | C -1 ; WX 502 ; N ecircumflex ; B 58 -10 447 643 ;
170 | C -1 ; WX 502 ; N igrave ; B 70 0 327 643 ;
171 | C -1 ; WX 502 ; N odieresis ; B 43 -10 459 593 ;
172 | C -1 ; WX 502 ; N otilde ; B 43 -10 459 618 ;
173 | C -1 ; WX 502 ; N notequal ; B 35 35 467 397 ;
174 | C -1 ; WX 502 ; N twosuperior ; B 123 301 394 618 ;
175 | C -1 ; WX 502 ; N Eth ; B 13 0 471 603 ;
176 | C -1 ; WX 502 ; N Adieresis ; B 5 0 497 723 ;
177 | C -1 ; WX 502 ; N Udieresis ; B 63 -15 440 723 ;
178 | C -1 ; WX 502 ; N eacute ; B 58 -10 447 643 ;
179 | C -1 ; WX 502 ; N ocircumflex ; B 43 -10 459 643 ;
180 | C -1 ; WX 502 ; N partialdiff ; B 70 -10 434 653 ;
181 | C -1 ; WX 502 ; N plusminus ; B 35 0 467 432 ;
182 | C -1 ; WX 502 ; N Atilde ; B 5 0 497 748 ;
183 | C -1 ; WX 502 ; N Idieresis ; B 70 0 432 723 ;
184 | C -1 ; WX 502 ; N Ucircumflex ; B 63 -15 440 773 ;
185 | C -1 ; WX 502 ; N egrave ; B 58 -10 447 643 ;
186 | C -1 ; WX 502 ; N zcaron ; B 55 0 447 643 ;
187 | C -1 ; WX 502 ; N oacute ; B 43 -10 459 643 ;
188 | C -1 ; WX 502 ; N ydieresis ; B 34 -161 479 593 ;
189 | C -1 ; WX 502 ; N degree ; B 160 437 340 618 ;
190 | C -1 ; WX 502 ; N Acircumflex ; B 5 0 497 773 ;
191 | C -1 ; WX 502 ; N Icircumflex ; B 70 0 432 773 ;
192 | C -1 ; WX 502 ; N Uacute ; B 63 -15 440 773 ;
193 | C -1 ; WX 502 ; N integral ; B 97 -121 406 618 ;
194 | C -1 ; WX 502 ; N ccedilla ; B 67 -161 449 452 ;
195 | C -1 ; WX 502 ; N ograve ; B 43 -10 459 643 ;
196 | C -1 ; WX 502 ; N infinity ; B 10 45 492 382 ;
197 | C -1 ; WX 502 ; N thorn ; B 73 -161 459 643 ;
198 | C -1 ; WX 502 ; N greaterequal ; B 35 -60 467 492 ;
199 | C -1 ; WX 502 ; N registered ; B 70 256 432 618 ;
200 | C -1 ; WX 502 ; N Delta ; B 13 0 489 603 ;
201 | C -1 ; WX 502 ; N Aacute ; B 5 0 497 773 ;
202 | C -1 ; WX 502 ; N summation ; B 20 0 496 603 ;
203 | C -1 ; WX 502 ; N Iacute ; B 70 0 432 773 ;
204 | C -1 ; WX 502 ; N Ugrave ; B 63 -15 440 773 ;
205 | C -1 ; WX 502 ; N Zcaron ; B 40 0 462 773 ;
206 | C -1 ; WX 502 ; N lessequal ; B 35 -60 467 492 ;
207 | C -1 ; WX 502 ; N aring ; B 55 -7 481 669 ;
208 | C -1 ; WX 502 ; N logicalnot ; B 35 121 467 322 ;
209 | C -1 ; WX 502 ; N Agrave ; B 5 0 497 773 ;
210 | C -1 ; WX 502 ; N ntilde ; B 74 0 431 618 ;
211 | C -1 ; WX 502 ; N Igrave ; B 70 0 432 773 ;
212 | C -1 ; WX 502 ; N multiply ; B 31 0 471 440 ;
213 | C -1 ; WX 502 ; N Scaron ; B 67 -15 454 773 ;
214 | C -1 ; WX 502 ; N adieresis ; B 55 -7 481 593 ;
215 | C -1 ; WX 502 ; N eth ; B 45 -10 458 643 ;
216 | C -1 ; WX 502 ; N scaron ; B 76 -10 429 643 ;
217 | C -1 ; WX 502 ; N udieresis ; B 72 -10 428 593 ;
218 | C -1 ; WX 502 ; N yacute ; B 34 -161 479 643 ;
219 | C -1 ; WX 502 ; N brokenbar ; B 221 -121 281 643 ;
220 | C -1 ; WX 502 ; N threequarters ; B 17 -15 515 618 ;
221 | C -1 ; WX 502 ; N Edieresis ; B 88 0 459 723 ;
222 | C -1 ; WX 502 ; N Odieresis ; B 25 -15 477 723 ;
223 | C -1 ; WX 502 ; N atilde ; B 55 -7 481 618 ;
224 | C -1 ; WX 502 ; N idieresis ; B 70 0 382 593 ;
225 | C -1 ; WX 502 ; N ucircumflex ; B 72 -10 428 643 ;
226 | C -1 ; WX 502 ; N underline ; B 0 -90 502 -30 ;
227 | C -1 ; WX 502 ; N minus ; B 35 186 467 246 ;
228 | C -1 ; WX 502 ; N onehalf ; B -1 -15 492 618 ;
229 | C -1 ; WX 502 ; N Omega ; B 20 0 482 618 ;
230 | C -1 ; WX 502 ; N radical ; B 0 -121 502 658 ;
231 | C -1 ; WX 502 ; N Ecircumflex ; B 88 0 459 773 ;
232 | C -1 ; WX 502 ; N Otilde ; B 25 -15 477 748 ;
233 | C -1 ; WX 502 ; N Ydieresis ; B 13 0 499 723 ;
234 | C -1 ; WX 502 ; N acircumflex ; B 55 -7 481 643 ;
235 | C -1 ; WX 502 ; N icircumflex ; B 70 0 402 643 ;
236 | C -1 ; WX 502 ; N uacute ; B 72 -10 428 643 ;
237 | C -1 ; WX 502 ; N zero1 ; B 45 -15 457 618 ;
238 | C -1 ; WX 502 ; N onequarter ; B 0 -15 492 618 ;
239 | C -1 ; WX 502 ; N Eacute ; B 88 0 459 773 ;
240 | C -1 ; WX 502 ; N trademark ; B 0 301 492 603 ;
241 | C -1 ; WX 502 ; N Ocircumflex ; B 25 -15 477 773 ;
242 | C -1 ; WX 502 ; N pi ; B 12 0 493 442 ;
243 | C -1 ; WX 502 ; N aacute ; B 55 -7 481 643 ;
244 | C -1 ; WX 502 ; N iacute ; B 70 0 367 643 ;
245 | C -1 ; WX 502 ; N ugrave ; B 72 -10 428 643 ;
246 | C -1 ; WX 502 ; N underscore1 ; B 33 -60 468 0 ;
247 | C -1 ; WX 502 ; N onesuperior ; B 139 301 285 618 ;
248 | C -1 ; WX 502 ; N product ; B 28 0 474 603 ;
249 | C -1 ; WX 502 ; N lozenge ; B 10 0 492 482 ;
250 | C -1 ; WX 502 ; N Egrave ; B 88 0 459 773 ;
251 | C -1 ; WX 502 ; N Oacute ; B 25 -15 477 773 ;
252 | C -1 ; WX 502 ; N divide ; B 35 0 467 432 ;
253 | C -1 ; WX 502 ; N agrave ; B 55 -7 481 643 ;
254 | C -1 ; WX 502 ; N approxequal ; B 35 60 467 372 ;
255 | C -1 ; WX 502 ; N mu ; B 73 -121 429 442 ;
256 | C -1 ; WX 502 ; N copyright ; B 25 -15 477 618 ;
257 | C -1 ; WX 502 ; N nbspace ; B 0 0 0 0 ;
258 | C -1 ; WX 502 ; N Ccedilla ; B 42 -161 470 618 ;
259 | C -1 ; WX 502 ; N Thorn ; B 93 0 471 603 ;
260 | C -1 ; WX 502 ; N Ograve ; B 25 -15 477 773 ;
261 | C -1 ; WX 502 ; N edieresis ; B 58 -10 447 593 ;
262 | EndCharMetrics
263 | EndFontMetrics
264 |
--------------------------------------------------------------------------------
/fig/deadlock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
284 |
--------------------------------------------------------------------------------
/fig/as.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
314 |
--------------------------------------------------------------------------------
/fig/switch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
330 |
--------------------------------------------------------------------------------
/fig/fslayout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
316 |
--------------------------------------------------------------------------------
/first.tex:
--------------------------------------------------------------------------------
1 | % terminology:
2 | % process: refers to execution in user space, or maybe struct proc &c
3 | % process memory: the lower part of the address space
4 | % process has one thread with two stacks (one for in kernel mode and one for
5 | % in user mode)
6 | % talk a little about initial page table conditions:
7 | % paging not on, but virtual mostly mapped direct to physical,
8 | % which is what things look like when we turn paging on as well
9 | % since paging is turned on after we create first process.
10 | % mention why still have SEG_UCODE/SEG_UDATA?
11 | % do we ever really say what the low two bits of %cs do?
12 | % in particular their interaction with PTE_U
13 | % sidebar about why it is extern char[]
14 |
15 | \chapter{オペレーティングシステムの構成}
16 | \label{CH:FIRST}
17 |
18 | オペレーティングシステムにとって重要な要求は,いろいろな活動を一度にで
19 | きることです.たとえば,\ref{CH:UNIX}章で述べたシステムコールインター
20 | フェースにおいて,プロセスは \lstinline{fork} を使って新しいプロセスを生む
21 | ことができます.オペレーティングシステムは,多数のプロセス間で,コン
22 | ピュータのリソースを\indextext{タイムシェア}(時間的に共有)しなくては
23 | いけません.たとえば,物理的な CPU よりも多数のプロセスがあったとしても,
24 | オペレーティングシステムはそれらの進行を保証しなくてはいけません.オペ
25 | レーティングシステムは加えて,プロセス間の\indextext{分離}も行わなくて
26 | はなりません.あるプロセスがバグでフェイル (fail) したとしても,別のプ
27 | ロセスに影響があってはいけません.しかし,完全な分離は強すぎます.プロ
28 | セス間で,意図的に相互通信したい場合もあるからです.パイプラインはその
29 | 一例です.オペレーティングシステムは,次の3つの要求を満たす必要がありま
30 | す.すなわち,マルチプレクス (multiplexing), 分離 (isolation), および相
31 | 互通信 (interaction) です.
32 |
33 | この章では,3 つの要求を満たすために,オペレーティングシステムがど
34 | のように構成されているかを概説します.さまざまな実現法がありえることが
35 | 明らかになりますが,この本では\indextext{モノリシックカーネル}を中心とする主流の設
36 | 計を述べます.この章はまた,xv6 の分離の単位であるプロセスと,
37 | xv6 の起動時における最初のプロセスの生成についても概説します.
38 |
39 | xv6 は,マルチコアの RISC-V マイクロプロセッサで動作するので,多くの低
40 | レベル機能(たとえばプロセスの実装)は RISC-V 固有です.RISC-V は64ビッ
41 | ト CPU です.xv6 は特に “LP64” C, すなわち long 型とポインタ(P)型が64ビッ
42 | トである一方,int 型は 32 ビットという C 言語で記述されています.読者に
43 | (何らかのアーキテクチャでの)マシンレベルのプログラミングの経験が少し
44 | あると想定し,RISC-V 固有のアイディアが出てきたらそれを紹介しま
45 | す.RISC-V に関する便利な参考文献は “The RISC-V Reader: An Open
46 | Architecture Atlas”~\cite{riscv} です.ユーザレベルの命令セットアーキテクチャと
47 | 権限付きアーキテクチャの公式の仕様は~\cite{riscv:user} と~\cite{riscv:priv} です.
48 |
49 | この文章は計算を実行する要素のことを基本的に \indextext{CPU} と呼びま
50 | す.Central Processing Unit の略です.他の文章(たとえば RISC-V の仕様
51 | 書)は,CPUのかわりに,プロセッサ,コア,および hart という用語を使うこ
52 | ともあります.xv6 は\indextext{マルチコア}の RISC-V のハードウェアを想
53 | 定します.それはすなわち,複数の CPU がメモリを共有しながら,独立のプロ
54 | グラムを並列で実行するということです.この文章はときどき,マルチコアと
55 | 同じ意味で\indextext{マルチプロセッサ}という用語を使います.厳密には,
56 | マルチプロセッサとは,1つの基板に複数のプロセッサチップが載ったものを表
57 | すこともあります.
58 |
59 | 完成品のコンピュータにおける CPU は,それを助けるためのハードウェアに囲まれており,
60 | そのほとんどは I/O インタフェースのためにあります.xv6 は,qemu に
61 | \texttt{-machine virt} オプションをつけてシミュレートできる
62 | ハードウェアをサポートします.それは,RAM, ブートコードが入った ROM,
63 | ユーザのキーボードや画面につながったシリアル通信,およびディスクストレージを
64 | 含みます.
65 |
66 |
67 | %%
68 | \section{物理的なリソースの抽象化}
69 | %%
70 |
71 | オペレーティングシステムに初めて出会ったときに最初に思い浮かぶ疑問は,
72 | なぜそんなものが必要なのか,ということかもしれません.図~\ref{fig:api} の
73 | システムコールは,アプリケーションプログラムをリンクするライブラリとして
74 | 実現することもできます.その場合,各アプリケーションは,
75 | そのアプリケーション専用のライブラリを使うことができます.アプリケーショ
76 | ンは,ハードウェアリソースに直接アクセスすることができ,そのアプリケー
77 | ションに最適な方法で使うことができます(そうすることで,高速化や,
78 | 予測可能な性能を達成できます).実際,組込機器向けのオペレーティングシステム
79 | やリアルタイム向けシステムには,そのような方法で構成されたものもありま
80 | す.
81 |
82 | ライブラリを用いたアプローチの欠点は,2 つ以上のアプリケーションが走っ
83 | ているとき,それらのアプリケーションは行儀よくしなくてはいけないという
84 | ことです.たとえば,各アプリケーションは,
85 | 他のアプリケーションが実行できるように
86 | 定期的に CPU を手放さなくてはなりません.各アプリケーションがお互いを信用して
87 | おり,かつバグが無いならば,
88 | そのような\textit{協調的}タイムシェリングはうまくいきます.しかし,アプリケーション同士は信頼しあっ
89 | ておらず,かつバグがあるのがより一般的な状況です.そのため,協調的な方
90 | 法よりも,より強い分離が欲しいときがあります.
91 |
92 | 強い分離を実現するよい方法は,アプリケーションに,繊細なハードウェアリソース
93 | を直接触らせないことです.そのかわり,リソースをサービスとして
94 | 抽象化するのです.たとえば,アプリケーションが生のディスクのセクタを直
95 | 接読み書きするかわりに,\lstinline{open}, \lstinline{read}, \lstinline{write},
96 | および \lstinline{close}システムコールを介してファイルシステムにアクセスさ
97 | せるのです.そのようにすることで,アプリケーションはパスや名前でアクセ
98 | スができて便利になるとともに,オペレーティングシステムは(そのインタ
99 | フェースの実装者として)ディスクを管理することができます.
100 |
101 | 同様に,Unix は複数のプロセスに対してハードウェアの CPU を透過的に切り
102 | 替えます.レジスタ状態を必要に応じて退避することで,アプリケー
103 | ションがタイムシェアリングであることを気にしなくてよいようにします.
104 | この透過性により,たとえ無限ループに陥るアプリケーションがいたとしても,
105 | オペレーティングシステムは CPU を共有することができます.
106 |
107 | Unix のプロセスが,物理メモリと直接やり取りするかわり
108 | に,\lstinline{exec} を使ってメモリイメージを構築するのは別の例です.そうすることで,
109 | オペレーティングシステムはメモリのどこにそのプロセスを置くか決めること
110 | ができるようになります.もしメモリが足りなかったら,オペレーティングシ
111 | ステムはプロセスのデータの一部をディスクに保存することもできま
112 | す.\lstinline{exec} により,実行可能なプログラムイメージを,ファ
113 | イルシステムに保存するという利便性もあります.
114 |
115 | Unix プロセス間の相互通信の多くは,ファイルディスクリプタを経由して行い
116 | ます.ファイルディスクリプタは,抽象化によって詳細(たとえばパイ
117 | プの中のデータやファイルはどこに保存されているか,など)を気にしなくて
118 | よくなるだけでなく,相互通信を単純化する方法も規定します.たとえば,
119 | パイプライン中のあるアプリケーションがフェイルした場合,カーネルはファイル
120 | 終端シグナルをパイプラインの次のプロセスに送ることができます.
121 |
122 | ここで見たように,図~\ref{fig:api} のシステムコールは緻密に設計されてお
123 | り,プログラマの便利さだけでなく,強い分離を行う機会を提供します.Unix
124 | インタフェースはリソースを抽象化する唯一の方法ではありませんが,とても
125 | 良いものであると知られています.
126 |
127 | %%
128 | \section{ユーザモード,スーパーバイザモード,およびシステムコール}
129 | %%
130 |
131 | 強い分離をするには,アプリケーションとオペレーティングシステムの間に明
132 | 確な境界線が必要です.もしあるアプリケーションがフェイルしても,オペ
133 | レーティングシステムや別のアプリケーションにはフェイルが伝搬しないで欲しい
134 | です.オペレーティングシステムはフェイルしたアプリケーションを
135 | 片づけ,他のアプリケーションを実行させ続けなくてはいけません.そのよう
136 | な強い分離を実現するには,あるアプリケーションがオペレーティングシステ
137 | ムのデータ構造や命令列を変更できなくする(あるいは見ることすらできなく
138 | する)とともに,別のアプリケーションのメモリにアクセスできなくする必要
139 | があります.
140 |
141 | 多くの CPU には,強い分離を行うためのハードウェア的な支援機能があります.
142 | たとえば,RISC-V は CPU が命令列を実行するのに3つのモードがあります.
143 | \indextext{マシンモード},\indextext{スーパーバイザモード},および\indextext{ユーザモード}です.マ
144 | シンモードで実行する命令列は無制限の権限を持ちます.起動直後の CPU はマ
145 | シンモードです.マシンモードは,ほとんどの場合,コンピュータのコンフィ
146 | ギュレーションで使います.xv6 がマシンモードで実行するのはわずか数行で,そのあとは
147 | スーパーバイザモードに切り替わります.
148 |
149 | スーパーバイザモードにおいて,CPU は\indextext{特権命令}を実行すること
150 | ができます.特権命令を用いると,たとえば,割込の有効化・無効化や,ペー
151 | ジテーブルのアドレスを指定するレジスタの読み書きなどができます.もしユー
152 | ザモードのアプリケーションが特権命令を実行しようとすると,CPU はその命
153 | 令を実行せず,かわりに,スーパーバイザモードに切り替わります.そして,
154 | するべきでないことをしようとしたアプリケーションを強制終了しま
155 | す.\ref{CH:UNIX} 章の図~\ref{fig:os} はそのような構成を示しています.
156 | アプリケーションはユーザモードの命令(たとえば足し算など)のみを実行す
157 | ることができ,それを「\indextext{ユーザ空間}で実行している」と言います.
158 | それに対し,スーパーバイザモードでは特権命令も実行することができ,
159 | 「\indextext{カーネル空間}で実行している」と言います.
160 | \indextext{カーネル}とは,カーネル空有で実行する(あるいはスーパーバイザモードで実行す
161 | る)ソフトウェアのことです.
162 |
163 | あるアプリケーションが,カーネルの関数(たとえば xv6 におけ
164 | る\lstinline{read} システムコール)を呼びたいと思ったら,カーネルに切り替
165 | えなくてはいけません.CPU が提供する特殊な命令を実行すると,ユーザモー
166 | ドからスーパーバイザモードが切り替わった上で,カーネルが指定するエント
167 | リポイントに飛びます (RISC-V では \indexcode{ecall} 命令を使います).CPU
168 | がスーパーバイザモードに切り替わると,カーネルはシステムコールの引数を
169 | 検証し,そのアプリケーションが実行して良い要求なのか判断し,実行するか
170 | 拒否します.スーパーバイザへの切り替えにおけるエント
171 | リポイントをカーネルが支配するのは重要です.もしアプリケーションがカー
172 | ネルのエントリポイントを選べたとしたら,悪意を持ったアプリケーションは,
173 | たとえば,引数の検証を回避できる場所を選ぶことができてしまいます.
174 |
175 |
176 | %%
177 | \section{カーネルの構成}
178 | %%
179 |
180 | オペレーティングシステムのどの部分をスーパーバイザモードで実行するかが,
181 | 設計上の重要な問いです.ひとつの方法は,オペレーティングシステム全体を
182 | カーネルに入れてしまい,システムコールの実装全体をスーパーバイザモードで
183 | 実行させることです.この構成を\indextext{モノリシックカーネル}と呼びます.
184 |
185 | この構成では,オペレーティングシステム全体が無制限のハードウェア特
186 | 権を持って実行されます.そうすると,ハードウェア特権が必要な場所とそ
187 | うでない場所を判断しなくてよくなるので,OS の設計者にとって楽で
188 | す.さらに,オペレーティングシステムの異なる部分同士が簡単に協調できる
189 | ようになります.たとえば,ファイルシステムと仮想メモリシステムの間に,
190 | 共有のバッファキャッシュを用意する場合などです.
191 |
192 | モノリシックカーネルの欠点は,オペレーティングシステムの異なる部分同士
193 | のインタフェースが複雑になりがちなことです(本書のあとの方で詳しく見ま
194 | す).そうすると,オペレーティングシステムの開発者が間違いを犯しやすくなり
195 | ます.モノリシックカーネルにおいて間違いは致命的です.スー
196 | パーバイザモードでは,たとえ小さな間違いであってもカーネルをフェイルさせうる
197 | からです.カーネルがフェイルするとコンピュータは止まってしまい,
198 | アプリケーションも当然フェイルします.復帰するには,コンピュータを再起動しなくては
199 | いけません.
200 |
201 | カーネルにおける間違いを減らすために,スーパーバイザモードで実行するコー
202 | ドを厳選し,オペレーティングシステムの大部分をユーザモードで走らせるこ
203 | ともできます.そのようなカーネルの構成法を\indextext{マイクロカーネル}と呼びます.
204 |
205 | \begin{figure}[t]
206 | \center
207 | \includegraphics[scale=0.5]{fig/mkernel.pdf}
208 | \caption{マイクロカーネルとファイルサーバ}
209 | \label{fig:mkernel}
210 | \end{figure}
211 |
212 | 図~\ref{fig:mkernel} にマイクロカーネルを図示します.図において,ファイルシステ
213 | ムはユーザレベルのプロセスとして走ります.プロセスとして走る OS のサー
214 | ビスをサーバと呼びます.アプリケーションがファイルサーバと相互通信で
215 | きるように,カーネルはプロセス間通信のメカニズムを提供することで,あるユーザ
216 | モードプロセスから別のユーザモードプロセスにメッセージを送れるようにし
217 | ます.たとえば,シェルのようなアプリケーションがファイルの読み書きをし
218 | たいと思ったら,そのアプリケーションはファイルサーバにメッセージを送り,
219 | その応答を待ちます.
220 |
221 | マイクロカーネルにおけるカーネルのインタフェースは,アプリケーション
222 | を開始する,メッセージを送る,デバイスのハードウェアにアクセスするなどのご
223 | く少ない低レベル関数からなります.そのような構成を取ると,オペレーティ
224 | ングシステムの大部分はユーザレベルのサーバとなるため,カーネルを比較的
225 | 単純にすることができます.
226 |
227 | xv6 は,多くの Unix オペレーティングシステムと同様に,モノリシックカー
228 | ネルとして実装します.xv6 のカーネルインタフェースはオペ
229 | レーティングシステムのインタフェースと同一であり,カーネルがオペレーティ
230 | ングシステム全体を実装します.xv6 はそこまで多くのサービスは提供しない
231 | ため,ある種のマイクロカーネルよりも小さいのですが,コンセプト上,
232 | xv6 はモノリシックです.
233 |
234 |
235 | \section{Code: xv6 の構成}
236 |
237 | \begin{figure}[t]
238 | \center
239 | {\footnotesize
240 | \begin{tabular}{l|l}
241 | \textbf{ファイル名} & \textbf{説明} \\ \midrule
242 | \texttt{bio.c} & ファイルシステムのためのディスクブロックキャッシュ.\\
243 | \texttt{console.c} & ユーザのキーボードと画面への接続.\\
244 | \texttt{entry.S} & 起動時の一番最初の命令列. \\
245 | \texttt{exec.c} & \texttt{exec()} システムコール.\\
246 | \texttt{file.c} & ファイルディスクリプタのサポート.\\
247 | \texttt{fs.c} & ファイルシステム. \\
248 | \texttt{kalloc.c} & 物理ページのアロケータ. \\
249 | \texttt{kernelvec.S} & カーネルおよびタイマ割込からのトラップの処理. \\
250 | \texttt{log.c} & ファイルシステムのロギングとクラッシュリカバリ.\\
251 | \texttt{main.c} & ブート時の他モジュールの初期化を制御. \\
252 | \texttt{pipe.c} & パイプ. \\
253 | \texttt{plic.c} & RISC-V の割込コントローラ. \\
254 | \texttt{printf.c} & 画面へのフォーマット付き出力. \\
255 | \texttt{proc.c} & プロセスとそのスケジューリング. \\
256 | \texttt{sleeplock.c} & CPU を手放すロック.\\
257 | \texttt{spinlock.c} & CPU を手放さないロック.\\
258 | \texttt{start.c} & 初期のマシンモードでのブートコード. \\
259 | \texttt{string.c} & C 文字列とバイト配列のためのライブラリ. \\
260 | \texttt{swtch.S} & スレッド切り替え. \\
261 | \texttt{syscall.c} & 実際に処理をする関数にシステムコールをディスパッチする. \\
262 | \texttt{sysfile.c} & ファイル関連のシステムコール. \\
263 | \texttt{sysproc.c} & プロセス関連のシステムコール. \\
264 | \texttt{trampoline.S} & ユーザとカーネルを切り替えるためのアセンブリコード. \\
265 | \texttt{trap.c} & トラップと割込を処理してリターンするための C コード. \\
266 | \texttt{uart.c} & シリアルポートコンソールのデバイスドライバ. \\
267 | \texttt{virtio\_disk.c} & ディスクのデバイスドライバ. \\
268 | \texttt{vm.c} & ページテーブルとアドレス空間の管理.
269 | \end{tabular}
270 | }
271 | \caption{Xv6 kernel source files.}
272 | \label{fig:source}
273 | \end{figure}
274 |
275 | xv6 のカーネルのソースは \texttt{kernel/} サブディレクトリに入っていま
276 | す.ソースは,荒くモジュール化され,ファイル分けされています.
277 | 図~\ref{fig:source} はファイルの一覧です.それぞれのモジュールとのインタフェースは,
278 | \lstinline{defs.h} \fileref{kernel/defs.h} で定義されています.
279 |
280 | %%
281 | \section{プロセスの概要}
282 | %%
283 |
284 | xv6 における分離の単位は(他の Unix オペレーティングシステムと同様
285 | に)\indextext{プロセス}です.プロセスによる抽象化を行うことで,あるプ
286 | ロセスが別のプロセスのメモリ, CPU, およびファイルディスクリプタなどを壊
287 | したり覗いたりできないようになります.同様に,プロセスがカーネルを破壊
288 | できないようにすることで,分離メカニズムを迂回できないようにします.カー
289 | ネルは,プロセスの抽象化を注意深く実装しなくてはいけません.さもなくば,
290 | バグっていたり悪意を持ったアプリケーションが,カーネルやハードウェアを
291 | だまして何か悪いことを引き起こすからです(例: 分離の迂回).カーネルが
292 | プロセスを実装するために用いるメカニズムには,ユーザモード・スーパーバ
293 | イザモードのフラグ,アドレス空間,スレッドのタイムスライスなどがありま
294 | す.
295 |
296 | 分離の強制力を高めるために,プロセスを抽象化することで,あるプ
297 | ログラムは,あたかもマシンを専有しているかのように見せます.
298 | プロセスはプログラムに対し,他のプロセスが読み書きすることができない,
299 | そのプロセス固有に見えるメモリシステム(あるいは\indextext{アドレス空間})を提供し
300 | ます.加えて,そのプログラムの命令列を専属で実行するかのように見える
301 | CPU を提供します.
302 |
303 | xv6 はページテーブル(ハードウェア的に実装されるもの)を用いて,各プロ
304 | セスに固有のアドレス空間を与えます.RISC-V のページテーブルは,
305 | \indextext{仮想アドレス}(RISC-V 命令が扱うアドレス)を,
306 | \indextext{物理アドレス}(CPU がメインメモリ
307 | に送るアドレス)に翻訳(あるいはマップ)します.
308 |
309 | \begin{figure}[t]
310 | \centering
311 | \includegraphics[scale=0.5]{fig/as.pdf}
312 | \caption{ユーザプロセスの仮想アドレス空間のレイアウト}
313 | \label{fig:as}
314 | \end{figure}
315 |
316 | ページテーブルはそのプロセスのアドレス空間を決めるもので,xv6 は,プロ
317 | セスごとに別のページテーブルを用意します.図~\ref{fig:as} に示すように,アドレス
318 | 空間には,そのプロセスの\indextext{ユーザメモリ}が,仮想アドレス0番から入っています.
319 | 最初に命令列,続いてグローバル変数,次にスタック,そして最後にヒープ領
320 | 域(malloc のためのもので,プロセスが必要に応じて増やせるもの)がありま
321 | す.xv6 は,仮想アドレス 39 ビットのRISC-V で動きますが,そのうち 38 ビッ
322 | トしか使いません.よって,最大長は $2^{38}-1$ = \texttt{0x3fffffffff}
323 | で,これは \lstinline{MAXVA}~\lineref{kernel/riscv.h:/MAXVA/} です.\ref{CH:TRAP}~章で詳しく見るよう
324 | に,アドレス空間の先頭には\indextext{トランポリン}のための 1 ページと,プロセ
325 | スの\indextext{トラップフレーム}がカーネルに切り替わるための1ページがあります.
326 |
327 | xv6 カーネルは,プロセスごとにさまざまな状態を保持しており,それら
328 | は\indexcode{proc} 構造体~\lineref{kernel/proc.h:/^struct.proc/} に収まっています.プロセスに
329 | とってもっとも重要なカーネルの状態は,ページテーブル,カーネルスタック,お
330 | よび実行状態です.以降は,\lstinline{proc}~構造体の各要素を示すために\indexcode{p->xxx} と
331 | いう記法を使います.たとえば,上述したトラップフレームは \indexcode{p->tf} です.
332 |
333 | 各プロセスは,そのプロセスの命令列を実行する実行スレッド(あるいは単純
334 | に\indextext{スレッド})を持ちます.スレッドは中断して,あとで再開できます.プ
335 | ロセスを透過的に切り替えるときには,カーネルは現在走っているスレッドを
336 | 中断し,別のプロセスのスレッドを再開します.スレッド内の状態(ローカル
337 | 変数,関数コールにおけるリターンアドレス)は,そのスレッド固有のスタックに
338 | 記憶されます.プロセスは 2 つのスタックを持ちます.1 つはユーザスタッ
339 | ク,もう 1 つはカーネルスタック(\indexcode{p->kstack}) です.プロセスがユー
340 | ザの命令列を実行しているときはユーザスタックのみが利用され,カーネルス
341 | タックは空です.(システムコールや割込により)プロセスがカーネルに入る
342 | と,カーネルのコードはそのプロセスのカーネルスタックを用いて実行を行います.
343 | あるプロセスがカーネルに入っているとき,ユーザスタックは記憶されたデータを
344 | 保持し続けますが,積極的には利用はされません.プロセスのスレッドは,
345 | ユーザスタックとカーネルスタックを行ったり来たりします.カーネルスタックは分離
346 | されているため(そしてユーザコードからは保護されているため),あるプロセスが
347 | 自身のスタックを壊してしまったとしても,カーネルは実行を継続できます.
348 |
349 | プロセスは,システムコールを呼ぶために RISC-V の \indexcode{ecall} 命令を
350 | 実行します.この命令は,ハードウェアの特権レベルを上げ,プログラムカウ
351 | ンタをカーネルが決めたエントリポイントに移します.システムコールが完了
352 | すると,カーネルはスタックをユーザスタックに切り替え,それか
353 | ら \indexcode{sret} 命令を呼んでユーザ空間にリターンします.\texttt{sret} 命令は,
354 | ハードウェア特権レベルを下げ,システムコール命令の直後の命令から実行を再
355 | 開します.プロセスのスレッドは,I/O を待つために,カーネルで「ブロック」す
356 | ることがあります.その場合,I/O が完了すると実行を再開します.
357 |
358 | \indexcode{p->state} は次のうちいずれかを表します: プロセスがアロケート済みである,
359 | 実行待ちである,実行中である,I/O を待っている,終了中である.
360 |
361 | \indexcode{p->pagetable} は,そのプロセスのページテーブルです.ペー
362 | ジテーブルは,RISC-V が指定するフォーマットで表現されます.
363 | あるプロセスをユーザ空間で実行し始めるとき,xv6 そのプロセス
364 | の \lstinline{p->pagetable} を使うようにページングハードウェアに指示します.
365 | プロセスのページテーブルは,そのプロセスのためにアロケートさ
366 | れた物理ページのアドレスの一覧にもなっています.
367 |
368 | %%
369 | \section{Code: xv6 の起動と最初のプロセス}
370 | %%
371 | xv6 をより具体化するために,カーネルがどのように開始し,そしてどうやって最初のプロセスを
372 | 起動するか概説します.続く章では,ここで出てくるメカニズムをより詳しく
373 | 説明します.
374 |
375 | RISC-V コンピュータの電源がオンになると,CPU は自身を初期化して,
376 | リードオンリーメモリに入ったブートローダを実行します.ブートローダは
377 | xv6 カーネルをメモリにロードします.そのあと,マシンモード状態の
378 | CPU は \indexcode{_entry} \lineref{kernel/entry.S:/^.entry:/} から
379 | 実行を始めます.xv6 が開始したとき,RISC-V のページングハードウェアはまだ無効であり,
380 | 仮想アドレスと物理アドレスは直接マップされています.
381 |
382 |
383 | ローダは,xv6 カーネルを物理アドレス \texttt{0x80000000} にロードします.
384 | \texttt{0x0} ではなく \texttt{0x80000000} から始めるのは,
385 | \texttt{0x0}〜\texttt{0x80000000} のアドレス範囲には I/O デバイスが
386 | 割りあたっているからです.
387 |
388 | \lstinline{_entry} にある命令列は,スタックを設定することで,xv6 が C コードを実
389 | 行できるようにします.xv6 は,最初のスタックである \lstinline{stack0}
390 | を \lstinline{start.c} で宣言していま
391 | す \lineref{kernel/start.c:/stack0/}.\lstinline{_entry} のコードは,スタックポインタレ
392 | ジスタ \lstinline{sp} に,スタックアドレス \lstinline{stack0+4096} を代入しま
393 | す.RISC-V のスタックは下向きに育つので,このアドレスがスタックの先頭となり
394 | ます.スタックが準備できたら,\lstinline{_entry} は\lstinline{start} の C コードに入りま
395 | す \lineref{kernel/start.c:/^start/}.
396 |
397 | その関数(たち)はマシンモードでしか許されていない各種コンフィギュレーションを行い,
398 | そのあとスーパーバイザモードに切り替えます.スーパーバイザモードに入るために,
399 | RISC-V には \lstinline{mret} 命令があります.この命令は,
400 | ほとんどの場合,どこかでスーパーバイザモードからマシンモードへの呼び出しが
401 | 行われたあと,元の場所にリターンするために使います.しかし,\lstinline{start} は
402 | そのような呼び出しからリターンするわけではありませんので,あたかもそうであったかの
403 | ように準備をします.そのために次を行います:
404 | (i) 前の特権がスーパーバイザであることを \lstinline{mstatus} レジスタにセッ
405 | トする,(ii) \lstinline{mepc} レジスタに main 関数のアドレスを書き込むこと
406 | で,返り先を main に指定する, (iii) ページテーブル用レジス
407 | タ \lstinline{satp} に 0 を書き込むことで,仮想アドレスへの変換を無効化す
408 | る,(iv) 全ての割込と例外をスーパーバイザモードに移譲する.
409 |
410 | スーパーバイザモードにジャンプする前に,もう1つやらなくてはいけないこと
411 | があります.タイマ割込を行うように,クロックチップに設定を書
412 | き込むことです.以上の面倒を見終わったら,\lstinline{starn} は \lstinline{mret}
413 | を呼ぶことでスーパーバイザモードへ「リターン」します.それにより,プログラムカウンタは
414 | \lstinline{main} \lineref{kernel/main.c:/^main/} に移ります.
415 |
416 | \lstinline{main} \lineref{kernel/main.c:/^main/} は,さまざまなデバイスとサブシステムの
417 | 初期化を追えると,\lstinline{userinit} を呼んで最初のプロセスを生成しま
418 | す \lineref{kernel/proc.c:/^userinit/}.最初のプロセスは小さなプログラムであ
419 | る\lstinline{initcode.S} \lineref{user/initcode.S:1} を実行します.それ
420 | は,\indexcode{exec} システムコールを呼んで再びカーネルに入ります.1章で見
421 | たように,\lstinline{exec} はそのプロセスのメモリやレジスタを,新しいプロ
422 | グラム(今回は \indexcode{/init})に取り替えます.カーネル
423 | が \lstinline{exec} を実行し終えると,そのプロセスはユーザ空間にリターンし
424 | て \lstinline{/init} の実行が始まります.\lstinline{init} \lineref{user/init.c:/^main/} は,必要
425 | に応じてコンソール用の新しいデバイスファイルを生成し,それをファイルディ
426 | スクリプタ0, 1, および 2番として開きます.そのあと,コンソール用シェルを
427 | 起動し,孤立したゾンビプロセスの面倒を見るためのループに入りま
428 | す.こうしてシステムは起動しました.
429 |
430 | %%
431 | \section{世の中のオペレーティングシステム}
432 | %%
433 |
434 | 現実の世界には,モノリシックカーネルとマイクロカーネルの両方があります.
435 | 多くの Unix カーネルはモノリシックです.たとえば,Linux はモノリシック
436 | カーネルです.ただし,一部の機能 (たとえばウィンドウシステム)はユー
437 | ザレベルサーバとして動きます.L4, Minix, および QNX のようなカーネルは
438 | サーバを用いるマイクロカーネルの構成を持ち,組込用途で広く使われていま
439 | す.
440 |
441 | 多くのオペレーティングシステムが,プロセスというコンセプトを採用してお
442 | り,それは xv6 のプロセスと似たものです.ただし,現代的なオペレーティン
443 | グシステムは,1つのプロセス内に複数のスレッドを持つことをサポートします.
444 | そうすることで,1つのプロセスが複数の CPU を使うことができるようになり
445 | ます.1つのプロセス内で複数のスレッドを動かせるようにするには,xv6 には
446 | ない多くのしかけ(たとえば,Linux は \lstinline{clone} とい
447 | う \lstinline{fork} の派生版を使います)を用いることで,プロセス内のスレッ
448 | ドが何を共有するか制御する必要があります.
449 |
450 | %%
451 | \section{練習問題}
452 | %%
453 |
454 | \begin{enumerate}
455 |
456 | \item gdb を使うと,カーネルからユーザへの,一番最初の切り替えを観察す
457 | ることができます.\texttt{make qemu-gdb} を実行してください.別のウィ
458 | ンドウにおいて,同じディレクトリで gdb を実行してください.gdb のコマ
459 | ンド \texttt{break *0x3ffffff07e} を実行することで,ユーザ空間にジャ
460 | ンプする \texttt{sret} 命令にブレークポイントを設定してください.gdb
461 | で \texttt{continue} コマンドを入力すると,gdb はそのブレークポイント
462 | において,\texttt{sret} を実行する直前で停止します.\texttt{stepi} を
463 | 入力すると,アドレス \texttt{0x4} 番に実行が移ることが,gdb で確認で
464 | きます.このアドレスはユーザ空間における,\texttt{initcode.S} の先頭を
465 | 指しています.
466 |
467 | % break *0x3ffffff000
468 | % disas 0x3ffffff000,+8
469 | % set disassemble-next-line auto
470 | % x/i $pc
471 | % break *0x3ffffff07e
472 | % print/x $sepc
473 | % print/x $pc
474 |
475 | \end{enumerate}
476 |
--------------------------------------------------------------------------------
/fig/bufrace.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
378 |
--------------------------------------------------------------------------------
/mem.tex:
--------------------------------------------------------------------------------
1 | % talk a little about initial page table conditions:
2 | % paging not on, but virtual mostly mapped direct to physical,
3 | % which is what things look like when we turn paging on as well
4 | % since paging is turned on after we create first process.
5 | % mention why still have SEG_UCODE/SEG_UDATA?
6 | % do we ever really say what the low two bits of %cs do?
7 | % in particular their interaction with PTE_U
8 | % sidebar about why it is extern char[]
9 | \chapter{ページテーブル}
10 | \label{CH:MEM}
11 |
12 | ページテーブルは,オペレーティングシステムが各プロセスに対し,そのプロセス
13 | 固有のアドレス空間とメモリを提供するためのメカニズムです.ページテーブルは,メ
14 | モリアドレスの意味と,物理メモリのどこにアクセスできるかを決めま
15 | す.xv6 はページテーブルを用いることで,異なるプロセス間のアドレス空間を分離する
16 | とともに,1つしかない物理メモリをマルチプレクスします.ペー
17 | ジテーブルが提供する間接的なアクセスは,次のようなトリックを実現
18 | するのにも役立ちます.同一のメモリ(トランポリンページ)を複数のアドレス
19 | 空間にマップするトリックや,未割り当てページを作ることでカーネルスタックとユー
20 | ザスタックの間をガードするトリックです.本章の残りは,RISC-V が提供する
21 | ページテーブルと,それを xv6 がどのように使うかを説明します.
22 |
23 | %%
24 | \section{ページングハードウェア}
25 | %%
26 | RISC-V の命令列は(ユーザもカーネルもどちらも)仮想アドレスを使います.それに対して
27 | マシンの RAM, すなわち物理メモリは物理アドレスを使います.RISC-V の
28 | ページテーブル用ハードウェアは,仮想アドレスを物理アドレスにマップする
29 | ことで,それら2種類のアドレスを結びつけます.
30 |
31 |
32 | \begin{figure}[t]
33 | \center
34 | \includegraphics[scale=0.5]{fig/riscv_address.pdf}
35 | \caption{RISC-V の仮想アドレスと物理アドレス}
36 | \label{fig:riscv_address}
37 | \end{figure}
38 |
39 | xv6 が動作するのは Sv39 RISC-V, すなわち 39 ビットの仮想アドレスを持つ
40 | ものです(図~\ref{fig:riscv_address} を見てください).64ビットの仮想アドレスのう
41 | ち,上位 25 ビットは使用しません.Sv39 のコンフィギュレーションにおい
42 | て,RISC-V のページテーブルは,論理的には,$2^{27}$ (134,217,728) 個の
43 | \indextext{ページテーブルエントリ} (PTE: Page Table Entries) からなる配列です.
44 | 各 PTE は,44 ビットの物理ページ番号 (PPN: Physical Page Number) と,各
45 | 種フラグを含みます.ページングハードウェアは,39 ビット中の上位 27 ビッ
46 | トをインデックスとして用いてページテーブルから PTE を探し,PPN の44 ビッ
47 | トと,仮想アドレス下位12ビットを連結して 56 ビットの物理アドレスを作り
48 | ます.すなわち,ページテーブルは,4096 ($2^{12}$) バイトに整列した塊を
49 | 粒度として,仮想・物理アドレスの変換を制御することができます.そのよう
50 | な塊を\indextext{ページ}と呼びます.
51 |
52 | \begin{figure}[t]
53 | \center
54 | \includegraphics[scale=0.5]{fig/riscv_pagetable.pdf}
55 | \caption{RISC-V のページテーブルのハードウェア}
56 | \label{fig:riscv_pagetable}
57 | \end{figure}
58 |
59 | Sv39 RISC-V において,仮想アドレスの上位25ビットは,アドレス変換には利
60 | 用しません.将来的に,RISC-V はそれらのビットを,さらなるアドレス変換の
61 | 階層のために利用するかもしれません.同様に,物理アドレスも増やす余地があります.
62 | Sv39 では 56 ビットですが,64 ビットまで増やすことができます.
63 |
64 | 図~\ref{fig:riscv_pagetable} に示すように,実際のアドレス変換は 3 ステップからなり
65 | ます.ページテーブルは 3 階層のツリーとして,物理メモリに保存されていま
66 | す.ツリーのルートは,4096 バイトのページテーブル用ページ (page-table
67 | page)です.このページは 512 個の PTE を含み,各エントリは次の階層のペー
68 | ジテーブル用ページの物理アドレスを指します.ツリーの最後の階層に至るま
69 | で,どのページも 512 個の PTE を含みます.ページテーブルのハードウェア
70 | は,ルートにあるページテーブル用ページから PTE を選択するインデックスと
71 | して,27 ビットのうち最上位 9 ビットを用います.その次の階層では中間
72 | の 9 ビットを,最後の階層では最後の 9 ビットをそれぞれ用います.
73 |
74 | もし,アドレス変換に必要な 3 つ PTE のうち 1 つでも足りなければ,ページ
75 | ング用ハードウェアはフォールト (fault) を発生させます.一般的な用途にお
76 | いて,仮想アドレスの大半は未割り当てです.上記のような 3 階層の構造を用
77 | いることで,未割り当ての範囲に対応するページテーブル用ページ全体を
78 | 省略し,メモリを節約できます.
79 |
80 | 各 PTE は,フラグビットを含んでおり,対応する仮想アドレスを使用してよい
81 | かどうかをページテーブル用ハードウェアに伝えるために利用されま
82 | す.\indexcode{PTE_V} は,PTE が存在するかどうかを示します.もしフラグが
83 | セットされていなければ,そのページへの参照はフォールトを生じます(すな
84 | わち許可されません).\indexcode{PTE_R} は,命令列が,そのページを読んで
85 | よいかどうかを指定します.また,\indexcode{PTE_W} は,命令列が,そのペー
86 | ジに書き込んでよいかどうかを指定します.\indexcode{PTE_X} は,そのページ
87 | の中身を命令として解釈し,実行して良いかどうかを指定しま
88 | す.\indexcode{PTE_U} は,ユーザモードで実行する命令列が,そのページアク
89 | セスできるかどうかを指定します.もし \indexcode{PTE_U} がセットされていな
90 | ければ,その PTE はスーパーバイザモードでのみ使うことができます.
91 | 図~\ref{fig:riscv_pagetable} に,以上がどのように動くか示します.各フラグと,ページ
92 | ング用ハードウェアに関連した構造体は \fileref{kernel/riscv.h} で定義されていま
93 | す.
94 |
95 | あるページテーブルを使うようにハードウェアに指示するためには,ルートと
96 | なるページテーブル用ページの物理アドレスを,\texttt{satp} レジスタに書
97 | き込みます.各 CPU は,固有の\texttt{satp}\index{satp@\lstinline{satp}} レジスタを持ちます.CPU は,
98 | 続く命令列が生成するアドレスを,その CPU 固有の \texttt{satp} が指すペー
99 | ジテーブルを用いてアドレス変換します.各 CPU が固有の \texttt{satp} を
100 | 持つので,各 CPU はそれぞれ別のプロセスを実行できます.
101 | プロセスは,ページテーブルが規定する固有のアドレス空間を持っています.
102 |
103 | 用語について注意をしておきます.物理メモリとは,DRAM 内部の記憶セルを意
104 | 味します.物理メモリ内のバイトはアドレスを持ち,それを物理アドレスと呼
105 | びます.命令は仮想アドレスしか扱いません.仮想アドレスはページング用ハー
106 | ドウェアによって物理アドレスに変換され,その上で DRAM に送られて読み書
107 | きに利用されます.仮想アドレスと物理アドレスの間を取り持つ,カーネルが
108 | 提供する抽象化のための機能の集まりのことを仮想メモリと呼びます.
109 |
110 |
111 | \begin{figure}[h]
112 | \centering
113 | \includegraphics[scale=0.65]{fig/xv6_layout.pdf}
114 | \caption{(左)xv6 のカーネルのアドレス空間.\texttt{RWX} は PTE の読
115 | み書き実行 (Read, Write, Execute) のパーミッションを示す.(
116 | 右)xv6 が想定する RISC-V の物理アドレス空間.}
117 | \label{fig:xv6_layout}
118 | \end{figure}
119 |
120 | %%
121 | \section{カーネルのアドレス空間}
122 | %%
123 | カーネルは,自身のためのページテーブルを持ちます.プロセスがカーネ
124 | ルに入ると,xv6 はカーネル用のページテーブルに切り替えます.そのあと,カー
125 | ネルがユーザ空間へ返るときは,xv6 はユーザプロセス用のページテーブルに
126 | 切り替え直します.カーネルのメモリはプライベートです.
127 |
128 | 図~\ref{fig:xv6_layout} はカーネルのアドレス空間のレイアウトと,仮想アドレスから物理アド
129 | レスへのマッピングを図示しています.あるファイル \fileref{kernel/memlayout.h}
130 | に,xv6 のカーネルのメモリレイアウトを規定する定数が宣言されています.
131 |
132 | QEMU は,I/O デバイス(たとえばディスクインタフェース)を含むコンピュー
133 | タをシミュレートします.QEMU は,デバイスのインタフェースを,
134 | \indextext{メモリマップ}された制御レジスタとしてソフトウェアに見せます.それらは,物理メモリ
135 | の \texttt{0x80000000} 未満にマップされます.カーネルはメモリ
136 | 領域を読み書きすることで,それらのデバイスとやりとりできます.\ref{CH:TRAP}~章で
137 | は,xv6 がどのようにデバイスとやりとりするかを説明します.
138 |
139 | カーネルは,仮想空間の大部分を,そのままマップします.すなわち,カーネ
140 | ルのアドレス空間のほとんどは,ダイレクトマッピングされています.たとえ
141 | ば,カーネル自身は,仮想アドレスでも物理アドレスでも \lstinline{KERNBASE}
142 | にあります.カーネルは通常の(仮想アドレスを用いた)ページの読み書きと,
143 | (物理アドレスを用いた)PTE 操作の両方を行う必要がありますが,ダイレ
144 | クトマッピングを用いることでそのコードを簡単にすることができます.
145 | ただし,ダイレクトマッピングされていない仮想アドレスもいくつかあります.
146 |
147 |
148 | \begin{itemize}
149 |
150 | \item トランポリンページ.仮想アドレス空間の先頭にマップされます.
151 | どのユーザのページテーブルでも同じマッピングを持ちます.\ref{CH:TRAP}~章において
152 | このトランポリンページの役割を説明しますが,この部分はページテーブルの
153 | 面白い利用例になっています.(トランポリンのコードを含む)物理ページは,
154 | カーネルの仮想アドレス空間に 2 つマップされます.1 つは仮想アドレス空間の
155 | 先頭であり,もう 1 つはカーネルの text 領域です.
156 |
157 | \item カーネルのスタックのページ.各プロセスは,固有のカーネルスタック
158 | を持ち,高位アドレス側にマップします.そうすることで,下位側に未割り当て
159 | の\indextext{ガードページ}を置くことができます.ガードページの PTE は無効で
160 | す(すなわち \lstinline{PTE_V} はセットされていません).そうすることに
161 | より,カーネルがカーネルのスタックをオーバーフローさせてしまった場合,
162 | 高い確率でフォールトを起こし,カーネルがパニックを起こして止まることが
163 | できます.もしガードページがなければ,オーバーフローはカーネルのメモ
164 | リを上書きし,不正な処理をしてしまう可能性があります.
165 | それならば,パニックでクラッシュする方がマシです.
166 |
167 | \end{itemize}
168 |
169 | カーネルは上位メモリへのマッピングを介してスタックを利用しますが,ダイ
170 | レクトマッピングされたアドレスを用いてもアクセスできます.ダイレクトマッ
171 | ピングだけを用意し,スタックをダイレクトマッピングされたアドレスで使う
172 | という設計もありえました.そのようにしてしまうと,ガードページを用意す
173 | るには仮想アドレスのアンマップが必要となって使いづらくなります.明示的に
174 | アンマップしない限り,物理メモリを参照するためです.
175 |
176 | カーネルはトランポリンのページとカーネルの text 領域を \lstinline{PTE_R}
177 | と\lstinline{PTE_X} のパーミッションでマップします.カーネルは,それらの
178 | ページを読むことと実行ができます.カーネルは,それ以外のページ
179 | は \lstinline{PTE_R} と \lstinline{PTE_W} でマップすることで,読み書きの両
180 | 方ができるようにします.ガードページは無効としてマップされます.
181 |
182 |
183 | %%
184 | \section{Code: アドレス空間を作る}
185 | %%
186 |
187 | アドレス空間とページテーブルの操作に関するコードのほとんど
188 | は,\texttt{vm.c} \lineref{kernel/vm.c:1} に入っています.中心となる構造体
189 | は \texttt{pagetable\_t} で,実はルートのページテーブル用ページへのポイ
190 | ンタです.\texttt{pagetable\_t} は,カーネルのページテーブルか,プロセ
191 | ス固有のページテーブルかのいずれかです.中心となる関数は,所与の仮想ア
192 | ドレスに対応する PTE を見つけるための \texttt{walk} と,新しいマッピン
193 | グのために PTE を設置する \texttt{mappages} です.カーネルのページテー
194 | ブルを操作する関数は \texttt{kvm} から,ユーザのページテーブルを操作する
195 | 関数は \texttt{uvm} から始まる関数を使います.どちらでもない関数は両方
196 | に使います.\texttt{copyout} と \texttt{copyin} は,システムコールの引
197 | 数として渡されたユーザの仮想アドレスを用いてデータを読み書きするために
198 | 使います.それらにカーネルからアクセスするには,対応する物理アドレスを
199 | 見つけるために明示的なアドレス変換が必要なので,\texttt{vm.c} に入って
200 | います.
201 |
202 | ブートシーケンスの初期で,\indexcode{main} は \indexcode{kvminit}
203 | \lineref{kernel/vm.c:/^kvminit/}
204 | を呼ぶことでカーネルのページテーブルを作ります.
205 | これは,xv6 が RISC-V のページングを有効化する前ですので,アドレスは物理
206 | アドレスを直接参照します.\lstinline{kvminit} はまず,ルートの
207 | ページテーブル用のページをアロケートします.そのあと,\indexcode{kvmmap} を呼ぶこと
208 | で,カーネルが必要とするアドレス変換をインストールします.ここで言うア
209 | ドレス変換は次の領域を含みます: カーネルの命令列とデー
210 | タ,\indexcode{PHYSTOP} までの物理メモリ,およびデバイスに対応するメモリ領
211 | 域.
212 |
213 | \indexcode{kvmmap} \lineref{kernel/vm.c:/^kvmmap/} は \indexcode{mappages}
214 | \lineref{kernel/vm.c:/^mappages/} を呼び,ある仮想アドレスの範囲から,対応する物理アド
215 | レスの範囲へのマッピングをページテーブルにインストールします.この関数
216 | は,範囲に含まれる仮想アドレスに対し,ページの周期ごとに,個々に処理を
217 | 行います.\lstinline{mappages} は
218 | 割り当てる仮想アドレスごとに \indexcode{walk} を呼び,対応す
219 | る PTE のアドレスを見つけます.続いて,対応する PTE を初期化します.
220 | すなわち,対応する物理ページ番号,所望のパーミッショ
221 | ン(\lstinline{PTE_W}, \lstinline{PTE_X}, および \lstinline{PTE_R}) を
222 | 設定するとともに,\lstinline{PTE_V} をセットしてその PTE を有効化します
223 | \lineref{kernel/vm.c:/perm...PTE_V/}.
224 |
225 | \indexcode{walk}
226 | \lineref{kernel/vm.c:/^walk/}
227 | は,RISC-V のページング用ハードウェアと
228 | 同じ方法で,仮想アドレスに対応する PTE を見つけます(図~\ref{fig:riscv_pagetable} を見てくださ
229 | い).\lstinline{walk} は,9ビットずつ使って 3 階層のページテーブルを下って
230 | いきます.各階層では,仮想アドレスの 9 ビットを使って,次の階層の,もし
231 | くは最終的なページの PTE を見つけます \lineref{kernel/vm.c:/pte.=..pagetable/}.もし PTE が無効
232 | だった場合は,必要なページがアロケートされていないということです.も
233 | し \lstinline{alloc} 引数がセットされていたら,\lstinline{walk} は新しいペー
234 | ジテーブル用ページをアロケートして,その物理アドレスを PTE に入れま
235 | す.\lstinline{walk} は,ツリーの最下層の PTE へのアドレスを返しま
236 | す \lineref{kernel/vm.c:/return..pagetable/}.
237 |
238 | 上記のコードは,物理メモリがカーネルの仮想アドレス空間にダイレクトマッ
239 | ピングされているという前提で動きます.たとえば,\texttt{walk} はページ
240 | テーブルの階層を下っていくとき,次のページテーブルの(物理)アドレス
241 | を PTE から見つけ \lineref{kernel/vm.c:/pagetable.=..pa.*E2P/},そのアドレスを仮想アドレスとして
242 | 用いて次の階層の PTE を得ます \lineref{kernel/vm.c:/t..pte.=..paget/}.
243 |
244 | \indexcode{main} は \indexcode{kvminithart}
245 | \lineref{kernel/vm.c:/^kvminithart/}
246 | を呼んで
247 | カーネルのページテーブルをインストールします.それにより,ルートの
248 | ページテーブル用ページの物理アドレスが,\texttt{satp} レジスタに書き込まれます.
249 | そのあと,CPU はページテーブルを用いてアドレス変換ができるようになります.
250 | カーネルはダイレクトマッピングを使っているので,次に実行する命令の仮想アドレスは,
251 | 正しい物理メモリのアドレスを指しています.
252 |
253 | \indexcode{procinit} \lineref{kernel/proc.c:/^procinit/} は,\lstinline{main} から呼び出される
254 | もので,プロセスごとのカーネルスタックをアロケートします.各スタックを,スタッ
255 | クのガードページの余裕を残しながら,\lstinline{KSTACK} で生成する仮想アド
256 | レスにマップします.\lstinline{kvmmap} は,新たにマッピングする PTE をカー
257 | ネルページテーブルに追加したあと,\lstinline{kvminithart} を呼んでそのカー
258 | ネルページテーブルを \lstinline{satp} にリロードすることで,
259 | ハードウェアに新しい \lstinline{PTE} を知らせます.
260 |
261 | RISC-V の各コアは,最近のページテーブルエントリを
262 | \indextext{Translation Look-aside Buffer} (\indextext{TLB}) にキャッシュするため,もしキャッシュされた TLB
263 | のエントリが期限切れになったときはそのことを教える必要があります.そうし
264 | ないと,TLB に残っていた古いマッピングを利用してしまう可能性があります.
265 | 古いマッピングが指している物理ページは,すでに別プロセスがアロケートし
266 | て使っているかもしれません.そのようなことが起きると,あるプロセスが別
267 | プロセスのメモリを上書きできてしまう問題を引き起こします.
268 | RISC-V は \indexcode{sfence.vma} 命令により,そのコアの TLB をフラッ
269 | シュ (flush) することができます.xv6 は,\texttt{kvminithart} におい
270 | て \texttt{satp} レジスタをリロードしたあと,ユーザ空間に戻る前に,ユー
271 | ザページテーブルに切り替えるためのトランポリンコードの内部
272 | で \texttt{sfence.vma} 命令を実行します \lineref{kernel/trampoline.S:/sfence.vma/}.
273 |
274 | \section{物理メモリのアロケート}
275 |
276 | カーネルは,実行時に,ページテーブル,ユーザメモリ,カーネルスタック,
277 | およびパイプバッファのためにメモリのアロケートと解放を行わなくてはなり
278 | ません.
279 |
280 | xv6 は,カーネルの終端から \indexcode{PHYSTOP} の間の領域を,実行時のメモ
281 | リアロケーションに利用します.4096 バイトのページ単位でアロケート・解放
282 | を行います.どのページが使用可能であるか追跡するために,ページ自体をリ
283 | ンクリストでつなぎます.ページをアロケートするときは,このリンクリストから
284 | ページを 1 つ取り除きます.一方,ページの解放は,このリンクリ
285 | ストに解放されたページをつなぎ加えます.
286 |
287 | %%
288 | \section{Code: 物理メモリのアロケータ}
289 | %%
290 |
291 | ページを確保するアロケータ (allocator) は,\texttt{kalloc.c}
292 | \lineref{kernel/kalloc.c:1}
293 | に入っています.アロケータのためのデータ構造は,割
294 | り当てることができる物理メモリページの\textit{自由リスト} (free list) です.リス
295 | トの要素は\indexcode{run} 構造体です \lineref{kernel/kalloc.c:/^struct.run/}. アロケータは,
296 | そのデータ構造を入れるためのメモリを,どこから入手したらよいでしょう
297 | か?\lstinline{run} 構造体は,その自由なページの中に記憶します.そのページ
298 | には他に記憶するものが無いので,\lstinline{run} 構造体を入れておいてもよい
299 | のです.自由リストはスピンロックで保護されま
300 | す \linerefs{kernel/kalloc.c:/^struct.{/,/}/}.
301 | リストとロックは構造体でくるまれており,どの
302 | ロックがどの領域を守っていることが分かるようになっています.当面
303 | は,ロックと \lstinline{lock} および\lstinline{acquire} の呼び出しのこ
304 | とは気にしないでください.ロックは \ref{CH:LOCK}~章で詳しく説明をします.
305 |
306 | \indexcode{main} 関数は,\indexcode{kinit} を呼ぶことで,アロケータを初期化し
307 | ます \lineref{kernel/kalloc.c:/^kinit/}.
308 | \lstinline{kinit} は,自由リストにカーネルの終
309 | 端から \lstinline{PHYSTOP} までの全ページを入れて初期化します.
310 | 本来ならば,コ
311 | ンフィギュレーション情報を解析することで,どれだけ物理メモリがあるのか
312 | 調べなくてはいけません.xv6 はそうするかわりに,そのマシン
313 | は 128 MB のRAM を持つと決め打ちしま
314 | す.\lstinline{kinit} は,\indexcode{freerange} を呼ぶことで,自由リストにメ
315 | モリを追加します.これは,内部的には,ページごとに \indexcode{kfree} を呼
316 | んで実現します.PTE は,4096 バイト境界に整列した物理アドレスだけを
317 | 指すことができます.
318 | \lstinline{freerange} は,\indexcode{PGROUNDUP} を用いることで,整列した
319 | 物理アドレスだけを解放することを保証します.アロケータはメモリが空の状態で
320 | 開始しますが,\lstinline{kfree} により,ある程度がその管理に入ります.
321 |
322 | アロケータは,アドレスを整数として扱って算術を行うことがありま
323 | す(例:\lstinline{freerange} における全ページの横断).一方で,アドレスをポ
324 | インタとして扱うことで,メモリへの読み書きを行うこともあります(例:各
325 | ページに入っている \lstinline{run} 構造体の操作).このように,アドレス
326 | を 2 つの意味で利用することが,アロケータのコードに C の型キャ
327 | スト\index{type cast}がたくさん含まれる理由です.また,もう1つの理由は,メモリの解放・アロケートが,原
328 | 理的にメモリの型を変更するためです.
329 |
330 | \lstinline{kfree} 関数 \lineref{kernel/kalloc.c:/^kfree/} は,まず,対応するメモリの全バ
331 | イトを 1 で埋めます.あるコードが解放済みのメモリを読み込んだ場合 (すな
332 | わち “dangling references” が置きた場合) に,過去のまともな内容ではなく,
333 | ゴミを読み込むようなります.そうすることで,問題のあるコードが早期に破
334 | 綻することが期待できます.\lstinline{kfree}は,対象のページを自由リストの
335 | 先頭に付け加えるために次のようにします: \lstinline{pa} を \lstinline{run} 構
336 | 造体へのポインタにキャストし,自由リストの古い先頭を \lstinline{r->next}
337 | に記録し,そして自由リストを \lstinline{r} にセットしま
338 | す.\indexcode{kalloc} は自由リストの先頭の要素を取り除き,そしてその要
339 | 素をリターンします.
340 |
341 | \section{プロセスのアドレス空間}
342 |
343 | 各プロセスは,独立したページテーブルを持ちます.xv6 がプロセスを切り替
344 | えるときは,ページテーブルも合わせて切り替えます.図~\ref{fig:as} に示すように,
345 | プロセスのユーザメモリは仮想アドレス 0 番から開始し,\texttt{MAXVA}
346 | \lineref{kernel/riscv.h:/MAXVA/} まで増やすことができます.そうすることで,プロセスは
347 | 原理的には 256 ギガバイトまでのメモリを使うことができます.
348 |
349 | プロセスが xv6 にさらなるメモリを要求したら,xv6 はまず\lstinline{kalloc}
350 | を使って物理ページをアロケートします.そのあと,プロセスのページテーブル
351 | に PTE を追加して,新たな物理ページを指し示すようにします.xv6 は,それ
352 | らの PTE に対し\lstinline{PTE_W}, \lstinline{PTE_X}, \lstinline{PTE_R},
353 | \lstinline{PTE_U}, および \lstinline{PTE_V} をセットします.ほとんどのプロ
354 | セスは,ユーザのアドレス空間を使い切ったりはしません.xv6 は,未使用
355 | の PTE に対しては,\lstinline{PTE_V} をクリア(ゼロ)のままにしておきま
356 | す.
357 |
358 | ページテーブルの使い方の良い例をいくつか見てみることにします.
359 | 第一に,異なるプロセスのページテーブルは,ユーザのアドレスを異なる物理
360 | メモリのページに変換するので,各プロセスはプライベートなメモリを持つこ
361 | とができます.第二に,各プロセスは,実際の物理メモリは不連続であったと
362 | しても,0 から始まる連続した仮想アドレスを持つことができます.
363 | 第三に,カーネルは,トランポリンコードをユーザのアドレス空間の先頭に
364 | マップします.そうすることで,物理メモリのあるページを,全てのアドレス空間
365 | に出現させることができます.
366 |
367 | \begin{figure}[t]
368 | \center
369 | \includegraphics[scale=0.5]{fig/processlayout.pdf}
370 | \caption{あるプロセスにおけるユーザのアドレス空間と,そのスタックの初期状態}
371 | \label{fig:processlayout}
372 | \end{figure}
373 |
374 | 図~\ref{fig:processlayout} は xv6 において実行中のプロセスのユーザメモリのレイアウトを,詳
375 | しく絵にしたものです.スタックは1ページで,\lstinline{exec} で作成された初
376 | 期の内容を表示しています.コマンドライン引数の文字列と,それらへのポイ
377 | ンタの配列がスタックの先頭にあります.そのすぐ下に置いてある値
378 | は,\lstinline{main} から開始したプログラムが,あたか
379 | も\lstinline{main(argc, argv)} として呼び出されたかのように見せるためにあ
380 | ります.
381 |
382 | ユーザスタックが,アロケートしたスタックメモリからオーバーフローしたら
383 | 気づけるように,xv6 は無効なガードページをスタックのすぐ下に置いておきま
384 | す.もしスタックがあふれてスタックの下のアドレスを使
385 | おうとしたら,無効なマッピングへのアクセスがハードウェアにページフォル
386 | トを生じさせます.現実世界のオペレーティングシステムには,あふれる前に,
387 | ユーザスタックのメモリを自動でアロケートするものもあります.
388 |
389 | \section{Code: sbrk}
390 |
391 | \lstinline{sbrk} はプロセスがメモリを増やしたり減らしたりするためのシステ
392 | ムコールです.これ
393 | は,\lstinline{growproc} 関数 \lineref{kernel/proc.c:/^growproc/} で実装さ
394 | れています.\lstinline{growproc} は,\lstinline{n} が正か負かに応じ
395 | て,\lstinline{uvmalloc} もしくは \lstinline{uvmdealloc} を呼び出しま
396 | す.\lstinline{uvmalloc} \lineref{kernel/vm.c:/^uvmalloc/} は,\lstinline{kalloc} で物理メモ
397 | リをアロケートし,\lstinline{mappages} によりユーザページテーブルに PTE を
398 | 足します.\lstinline{uvmdealloc} は \lstinline{uvmunmap} \lineref{kernel/vm.c:/^uvmunmap/} を
399 | 呼びます.それは,\lstinline{walk} を用いて PTE を見つけ,\lstinline{kfree}に
400 | より対応する物理メモリを解放します.
401 |
402 | プロセスのページテーブルは,ハードウェアに対して仮想アドレスのマッ
403 | ピング方法を指示するために利用しますが,そのプロセスにどの物理ページがアロケートされて
404 | いるかを示す唯一の記録でもあります.\texttt{uvmunmap} にお
405 | いてユーザメモリを解放するときに,ユーザページテーブルを見に行かなくてはいけない
406 | のはそのためです.
407 |
408 | %%
409 | \section{Code: exec}
410 | %%
411 | \lstinline{exec} はアドレス空間の,ユーザに属する部分を作るためのシステム
412 | コールです.その部分を,ファイルシステムに記録されたファイルを用いて初
413 | 期化します.\lstinline{exec} \lineref{kernel/exec.c:/^exec/} は,\ref{CH:FS}~章で説明するよう
414 | に,\indexcode{namei} \lineref{kernel/exec.c:/namei/} により名前付きのバイナリのパスを
415 | 開きます.そのあと,ELF ヘッダを読み込みます.xv6 のアプリケーションは,
416 | 広く使われる \indextext{ELF フォーマット}で記述されており,それは \fileref{kernel/elf.h} で
417 | 定義されています.ELF バイナリは,ELF ヘッダである \indexcode{elfhdr} 構造
418 | 体 \lineref{kernel/elf.h:/^struct.elfhdr/} と,プログラムのセクションヘッダ \lstinline{proghdr}
419 | 構造体 \lineref{kernel/elf.h:/^struct.proghdr/} の列からなります.各 \lstinline{proghdr} は,メ
420 | モリにロードされるべきプログラムの1セクションを記述します.xv6 のプログ
421 | ラムには,プログラムセクションヘッダが 1 つしかありませんが,命令列やデータ
422 | に対して別のセクションを割り当てるシステムもあります.
423 |
424 | 最初のステップは,ファイルが ELF バイナリであるかどうかの簡単なチェック
425 | です.ELF バイナリの先頭は,4 バイトのマジックナンバー \lstinline{0x7F},
426 | .code 'E',
427 | .code 'L',
428 | .code 'F',
429 | あるい
430 | は \indexcode{ELF_MAGIC} です
431 | \lineref{kernel/elf.h:/ELF_MAGIC/}.もしELF ヘッダが正しいマ
432 | ジックナンバーを持つならば,\lstinline{exec} はそのバイナリが正しいフォー
433 | マットを持つと考えます.
434 |
435 | \lstinline{exec} は \indexcode{proc_pagetable} \lineref{kernel/exec.c:/proc_pagetable/} により,ユー
436 | ザによる割当のない新しいページテーブルを得ます.そのあと,各 ELF セグメン
437 | トのメモリを \indexcode{uvmalloc} \lineref{kernel/exec.c:/uvmalloc/} でアロケート
438 | し,\indexcode{loadseg} \lineref{kernel/exec.c:/loadseg/} によりセグメントの内容をメモリ
439 | にロードします.\lstinline{loadseg} は,\indexcode{walkaddr} を用いて
440 | アロケートしたメモリの物理アドレスを調べ,ELF セグメントの各ページを書きこみます.
441 | また,\indexcode{raedi} により,ファイルの読み込みを行います.
442 |
443 | \lstinline{exec} が作る最初のプログラムである
444 | \indexcode{/init} のプログラムセクションヘッダは以下の通りです.
445 | %
446 | \begin{footnotesize}
447 | \begin{verbatim}
448 | # objdump -p _init
449 | user/_init: file format elf64-littleriscv
450 |
451 | Program Header:
452 | LOAD off 0x00000000000000b0 vaddr 0x0000000000000000
453 | paddr 0x0000000000000000 align 2**3
454 | filesz 0x0000000000000840 memsz 0x0000000000000858 flags rwx
455 | STACK off 0x0000000000000000 vaddr 0x0000000000000000
456 | paddr 0x0000000000000000 align 2**4
457 | filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
458 | \end{verbatim}
459 | \end{footnotesize}
460 |
461 | プログラムのセクションヘッダにおけ
462 | る \lstinline{filesz} は,\lstinline{memsz} よりも小さいときがあり,その差の
463 | 領域はファイルから読むこむかわりに,ゼロで埋めなくてはいけません(C の
464 | グローバル変数のためです).\lstinline{/init} においては,
465 | \lstinline{filesz} は 2112 バイトで,\lstinline{memsz} は 2136 バイトです.
466 | よって,\indexcode{uvmalloc} は 2136 バイトが入るのに十分な物理メモリを
467 | 確保しますが,\lstinline{/init} ファイルからは 2112 バイトしか読みません.
468 |
469 | 続いて,\indexcode{exec} はユーザスタックを初期化します.そのために,1ペー
470 | ジだけ確保します.\lstinline{exec} は,引数の文字列をスタックの先頭に 1 つ
471 | ずつコピーし,それらへのポインタを \indexcode{ustack} により記録します.さら
472 | に,\indexcode{argv} リストとして \lstinline{main} に渡されるものの最後には,
473 | ナルポインタを置きます.\lstinline{ustack} の最初の 3 エントリはフェイクの
474 | リターンアドレス,\indexcode{argc},および \lstinline{argv} へのポインタで
475 | す.
476 |
477 | \lstinline{exec} は,アクセス不可能なページを,先のスタックページのすぐ下
478 | に置きます.そうすることで,1ページ以上使用しようとしたプログラムはフォ
479 | ルトを起こすようにします.このアクセス不可能ページは,長すぎる引数にも
480 | 対処します.そのようなとき,\lstinline{exec} が引数をスタックへコピーす
481 | るのに使う \indexcode{copyout} 関数 \lineref{kernel/vm.c:/^copyout/} は,コピー先のページ
482 | がアクセス不可能であることに気づいて \-1 をリターンします.
483 | 新しいメモリイメージを準備するあいだ,もし \lstinline{exec} が不正なプログ
484 | ラムセグメントなどのエラーを見つけたら,\lstinline{bad} というラ
485 | ベルにジャンプし,新しいイメージを解放し,そして \-1 をリターンしま
486 | す.\lstinline{exec} は,そのシステムコールが成功すると確認できるまで,古
487 | いイメージの解放は待たなくてはいけません.もし古いイメージが無く
488 | なってしまうと,システムコールは \-1 を返すことができないからです.
489 | \lstinline{exec} のエラーが起きるのは,イメージを作る途中に限ります.もしイ
490 | メージが完成したら,\lstinline{exec} は新しいページテーブルを手に入れ \lineref{kernel/exec.c:/pagetable.=.pagetable/},古
491 | いものを解放することができます \lineref{kernel/exec.c:/proc_freepagetable/}.
492 |
493 | \lstinline{exec} は ELF ファイルに入っているバイト列を,ELF ファイルが指定
494 | するアドレスに従ってメモリにロードします.ユーザもしくはプロセスは, 好
495 | きなアドレスを ELF ファイルに書くことができます.ELF ファイルは事故もし
496 | くは悪意によってカーネルを指すことがありうるので,\lstinline{exec} には危
497 | 険性があります.カーネルの不注意は,クラッシュから悪意を持ったカーネル
498 | の分離メカニズムの迂回(すなわちセキュリティのエクスプロイト)にいたる
499 | までさまざまな結果をもたらします.xv6 は,いくつものチェックを行うこと
500 | で,そのリスクを避けます.たとえば,
501 | %
502 | \lstinline{if(ph.vaddr + ph.memsz < ph.vaddr)}
503 | %
504 | は,加算結果が64ビット整数からあふれるかどうかをチェックします.ありう
505 | る危険は次のようなものです: ユーザが選んだアドレスとし
506 | て \lstinline{ph.vaddr} が入った ELF バイナリを作ることができて,か
507 | つ \lstinline{ph.memsz} が十分大きくて加算結果があふれ
508 | て \lstinline{0x1000}になってしまう場合,あふれた結果が正しい値に見えて
509 | しまう.xv6 の古いバージョンでは,ユーザのアドレス空間にはカーネルが
510 | (ユーザモードでは読み書きできない状態で)マップされていたので,ユーザ
511 | はカーネルのメモリに対応するアドレスを選ぶことで, ELF バイナリのデータ
512 | からカーネルへデータをコピーできました.この RISC-V のバージョンの xv6
513 | では,そのようなことは起きません.カーネルは独自のページテーブルを持っ
514 | ており,\lstinline{loadseg} はカーネルではなくプロセスのページテーブル
515 | に対するロードを行うためです.
516 |
517 | カーネル開発者は,重要なチェックを怠ってしまうことがあります.そして,
518 | 現実世界のカーネルでは,カーネル特権の奪取につながるチェックの不在に
519 | 長い歴史があります.xv6 にも,ユーザレベルプログラムからカーネルに届く
520 | データの検証が完全でない部分があり,悪意あるユーザプログラムが xv6 の
521 | 分離を迂回するエクスプロイトを作ることができるかもしれません.
522 |
523 | %%
524 | \section{世の中のオペレーティングシステム}
525 | %%
526 |
527 | ほとんどのオペレーティングシステムと同じく,xv6 はページングハードウェ
528 | アをメモリ保護とマッピングに使います.ほとんどオペレーティングシステム
529 | は,xv6 よりはるかに洗練されたページングの使い方をします.たとえば,xv6
530 | にはディスクからのデマンドページング,\texttt{fork} におけるコピーオン
531 | ライト,ページの遅延確保(lazily-allocated pages), 自動で伸びるスタッ
532 | ク,メモリマップドファイル (memory-mapped file) などがありません.
533 |
534 | RISC-V は物理アドレスのレベルでの保護もサポートしますが,xv6 はその機能を
535 | 使いません.
536 |
537 | メモリが大量にあるマシンにおいては,RISC-V のスーパーページ(サイズの大
538 | きいページ)にご利益があるかもしれません.一方,物理メモリが小さいとき
539 | は,小さいページを使うほうが有利です.ページ確保やディスクへのページ
540 | アウトを細かい粒度で行うことができるからです.たとえば,メモリを8キロバイト
541 | 使うプログラムがあった場合,そのようなプログラムに,4メガのスーパーページ全体を
542 | 割り当てるのは無駄です.大きなページは,RAM をたくさん積んだマシンにおいて
543 | ご利益があり,ページテーブル操作のオーバーヘッドを減らせる可能性があります.
544 |
545 |
546 | xv6 カーネルには,\texttt{malloc} のような小さなオブジェクト用のメモリアロケータ
547 | がありません.そのためカーネルは,動的なメモリ確保が必要な,洗練
548 | されたデータ構造を使うことができません.
549 |
550 | メモリ確保は,大昔のホットトピッ
551 | クでした.予測できない将来のリクエストに備えて,限られたメモリを効率的
552 | に使いたい,というのが基本的な問題でした~\cite{knuth}.しかし今日では,空間的な効率性よ
553 | りも速度が優先されます.また,より発展したカーネルは,xv6 のよう
554 | な4096 バイトブロック単位ではなく,サイズの異なる小さなメモリブロックを
555 | 大量に確保します.実世界におけるカーネルのアロケータは,大きなメモリだけでなく,
556 | 小さなメモリのアロケートの面倒も見る必要があるのです.
557 |
558 | %%
559 | \section{練習問題}
560 | %%
561 |
562 | \begin{enumerate}
563 |
564 | \item RISC-V のデバイスツリーを解析し,そのコンピュータが持つ物理メモリの量を求めてください.
565 |
566 | \item \lstinline{sbrk(1)} を呼ぶことで,アドレス空間を 1 バイト増加させる
567 | ユーザプログラムを書いてください.そのプログラムを実行し,\lstinline{sbrk}
568 | 呼び出しの前後におけるページテーブルを調査してください.カーネルがアロケートした
569 | 空間はどれだけでしょうか?新しいメモリに対応する \lstinline{pte} の中身は
570 | どうなっているでしょうか?
571 |
572 | \item xv6 を改造して,カーネルがスーパーページを使えるようにしてください.
573 |
574 | \item xv6 を改造して,ユーザプログラムがナルポインタをデリファレンスし
575 | たときにフォールトを受け取るようにしてください.すなわち,全てのプログラムに
576 | おいて,仮想アドレス 0 番がマップされないようにしてください.
577 |
578 | \item Unix における \lstinline{exec} は,伝統的に,シェルスクリプトを
579 | 特別扱いします.実行するファイルが \lstinline{\#!} から始まる場合,
580 | その 1 行目はファイルを解釈するためのプログラムと認識されます.
581 | たとえば,\lstinline{myprog arg1} を実行するために \lstinline{exec} が
582 | 呼ばれ,\lstinline{myprog} の 1 行目が \lstinline{\#!/interp} で
583 | あったら,\lstinline{exec} は \lstinline{/interp myprog arg1} という
584 | コマンドで \lstinline{/interp} を実行します.xv6 に,以上の機能を実装してください.
585 |
586 | \item カーネルに,アドレス空間のランダム化を実装してください.
587 |
588 | \end{enumerate}
589 |
590 |
--------------------------------------------------------------------------------