Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Unify the level of headlines. #4

Merged
merged 1 commit into from about 1 year ago

2 participants

Taiki Kawakami Shinpei Maruyama
Taiki Kawakami

見出しのレベルを統一しました。

ファイルの先頭にある見出しはこのレベル

その他の見出しはこのレベル

に変更しました。どうでしょうか。

(これはePub 化の布石です)

Shinpei Maruyama
Owner

:+1::+1::smile::100::8ball::star2:

Shinpei Maruyama Shinpeim merged commit 9c18f3c into from March 24, 2013
Shinpei Maruyama Shinpeim closed this March 24, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Mar 25, 2013
Taiki Kawakami Unify the level of headlines. ea7b655
This page is out of date. Refresh to see the latest.
10  001.md
Source Rendered
... ...
@@ -1,6 +1,6 @@
1  
-# 導入
  1
+## 導入
2 2
 
3  
-## マルチプロセスとスケジューリング
  3
+### マルチプロセスとスケジューリング
4 4
 
5 5
 \*nix系のシステムは、もともと複数のユーザーが同じコンピューターリソース(CPUやメモリー)を同時に使うことを前提に作られています。そのため、\*nix系のシステムでは様々な処理が同時に行われるような仕組みになっています。実際、小規模なwebサービスでは nginx と unicorn と MySQL がひとつのマシンで同時に走っているような状況は珍しくないのではないでしょうか。
6 6
 
@@ -15,7 +15,7 @@ OSは、上記のように処理A,B,Cを順々に切り替えながら少しず
15 15
 
16 16
 ちなみに、この切り替えをどういう戦略やタイミングで行うかのことを、「スケジューリング」と言います。このへんはOSが面倒を見てくれますが、niceというコマンドで「これははあんまり重要じゃないプロセスなんで、優先度低めでいいよ」という情報をOSに教えたりできて、そうするとOSさんはそのプロセスを処理する時間を少なめにスケジューリングしたりします。
17 17
 
18  
-## マルチコアとの関係
  18
+### マルチコアとの関係
19 19
 
20 20
 最近のマシンのCPUはコアが複数あるのが普通です。コアがひとつだけならば、ひとつのコアで全てのプロセスをスケジューリングする必要があるわけですが、コアが複数あるため、下記のような感じで今度は「ほんとうの」同時処理が可能になります。
21 21
 
@@ -31,6 +31,6 @@ OSは、上記のように処理A,B,Cを順々に切り替えながら少しず
31 31
 
32 32
 ただ、スレッドに関しては、OSが面倒を見てくれるスレッド(いわゆるネイティブスレッド)と、例えば言語処理系やVMが面倒見てくれるスレッド(いわゆるグリーンスレッド)があって、グリーンスレッドの中にはいくらスレッドを増やしてもコアをひとつしか使えないようなものもあります。CRubyの1.8などがその例ですね。スレッドと一口に言ってもどのような実装になっているかによって特徴が変わってくるので、自分が使っている環境の「スレッド」というのがどのような仕組みをさしているのかは意識しておく必要があるでしょう。
33 33
 
34  
-## 次回予告
  34
+### 次回予告
35 35
 
36  
-次回はプロセスについてもう少し深くまでもぐって見ていきます。
  36
+次回はプロセスについてもう少し深くまでもぐって見ていきます。
18  002.md
Source Rendered
... ...
@@ -1,6 +1,6 @@
1  
-# プロセスの生成
  1
+## プロセスの生成
2 2
 
3  
-## プロセスの例
  3
+### プロセスの例
4 4
 
5 5
 前回、プロセスとはOSが処理を切り替えるときの処理の単位だという話をしましたが、まずはプロセスの例を見てみましょう
6 6
 
@@ -41,7 +41,7 @@
41 41
 
42 42
 でフォアグラウンドに処理を戻して、 Ctrl+C かなんかで処理を止めましょう。その後再度 ps コマンドでプロセスの一覧を確認すると、perlのプロセスが無くなっていることが確認できるかと思います。
43 43
 
44  
-## プロセスのライフサイクル
  44
+### プロセスのライフサイクル
45 45
 
46 46
 プロセスは、なんらかの方法で生成されたあとは、ぐんぐん処理を行っていき、処理が終わったり外部から止められたりすると消滅します。
47 47
 
@@ -55,7 +55,7 @@
55 55
 
56 56
 「えっ待ち状態とブロック中ってなにが違うの」という疑問を持ったかた、ごもっともです。でも、その違いは簡単です。「待ち状態」というのは、「もうすぐにでも処理できるよ!CPUさん、はやくわたしを処理して!」という状態のことです。一方、「ブロック中」というのは、たとえばファイルの読み込みを行うときにdisk I/Oを待っているなどで、「今CPUさんが私を処理しようとしても私まだIO待ちだから何もできないよ!」みたいな状態のことです。
57 57
 
58  
-## fork
  58
+### fork
59 59
 
60 60
 さて、さきほど簡単に「プロセスをなんらかの方法で生成」と言いましたが、たとえば新しくコマンドを叩いて新しいプロセスが生成されるとき、中では何が起きてるのでしょうか?
61 61
 
@@ -69,7 +69,7 @@
69 69
 
70 70
 ちなみに、forkは複数行うことができるので、「子だくさん」なプロセスというのも、あり得ます。preforkのサーバープロセスなんかは子供をたくさん作って、複数の接続のひとつひとつをそれぞれひとつの子供に処理させることで並列性を上げているわけですね。子供たちを酷使するひどいやつです。
71 71
 
72  
-## プロセスツリー
  72
+### プロセスツリー
73 73
 
74 74
 さきほど「親プロセスがforkで子プロセス作るんだよ〜〜。だからみんな親がいるんだよ〜〜〜」ってゆるふわな感じで言いましたが、当然「えっじゃあ、その親プロセスは誰が作ったの?」という疑問がわいてきますよね。疑問にお答えしましょう。親プロセスは、「親プロセスの親プロセス」がforkで作ったのです。となると、当然「えっじゃあ、その『親プロセスの親プロセス』はだれが作ったの」いう疑問がわいてきますよね。もちろん、「親プロセスの親プロセスの親プロセス」がforkで作ったのです。となると当然(ry
75 75
 
@@ -80,7 +80,7 @@
80 80
 
81 81
 このように、プロセスは親子関係の木構造を持っています。この親子関係を「プロセスツリー」と呼びます。プロセスツリーがどうなっているかを調べるためにpstreeというコマンドが使えますので、興味があればpstreeコマンドでどのようなプロセスツリーが生成されているか見てみるのもよいかと思います。pstree コマンドの使いかたはmanで調べてください(丸投げ)
82 82
 
83  
-## exec
  83
+### exec
84 84
 
85 85
 さて、「すべてのプロセスは祖先からforkされて生まれた」という話と「forkは親プロセスをまるっとコピーして子プロセスを作る」という話をしましたが、これ、なんかおかしいですね。そうです。このままでは、「親の複製のプロセス」しかなくって、すべてが同じことを行うプロセスになってしまいます!
86 86
 
@@ -93,7 +93,7 @@
93 93
 
94 94
 ことで、親プロセスとは違うプロセスをどんどん生成していくような仕組みになっているわけです。
95 95
 
96  
-##
  96
+###
97 97
 
98 98
 「日本語だとよくわかんないよ、コードで書いてよ」という声がわたしの脳内から聞こえてきたので、コードで書きます。
99 99
 
@@ -158,9 +158,9 @@ psコマンドの出力に、上記のようなふたつの行が見つかるか
158 158
 
159 159
 今は、「forkで子プロセスを生成できて、execでそのプロセスの内容を書き換えられた」ということがわかれば十分です。コマンドを叩いて新しいプロセスを生成する場合とかも、内部ではこのようにforkでプロセスを生成して、確保された環境の内容をexecで書き換えるという形で生まれているのです。ちなみに、シェルからコマンドを叩いてプロセスを生成するときには、「親プロセス」に当たるのはシェルのプロセスになります。
160 160
 
161  
-## 今後の予定
  161
+### 今後の予定
162 162
 
163 163
 * forkしたpidを看取る話と子供がゾンビになっちゃう話
164 164
 * あらゆる入出力はファイルとして扱われてるよって話からの、forkした際の file descripter と open file description について
165 165
 
166  
-あたりを書きたい気持ちがある
  166
+あたりを書きたい気持ちがある
22  003.md
Source Rendered
... ...
@@ -1,4 +1,4 @@
1  
-# プロセスとファイル入出力
  1
+## プロセスとファイル入出力
2 2
 
3 3
 さて、前回、プロセスというのは「自分が独占したメモリーの中で動いているので、その中で何をしても他のプロセスのメモリーに影響を与えない」というのを見れたかと思います。でも、そんな自分の中だけで完結してる引きこもりみたいなプロセスじゃあ、意味がないですね。外界からなんかデータをもらって、自分の中で処理して、それを外の世界に知らせる方法が必要になってきます。
4 4
 
@@ -37,13 +37,13 @@ nyan_copy.rbを、nyan.txtと同じディレクトリに作って、実行して
37 37
 
38 38
 こうして、プロセスはファイルを通じて外部との入出力を行うことができます。
39 39
 
40  
-# すべてがファイル???
  40
+### すべてがファイル???
41 41
 
42 42
 さて、いまは「テキストファイル」への読み書きを行ってみましたが、「Linuxではすべてがファイルなんだよ」みたいな話を聞いたことがないでしょうか? そんなこと言われても、「はっ?」って感じの話ですよね。「Linuxではキーボードもファイルだからね」みたいなことを言うひとに至っては「こいつ頭大丈夫か、キーボードはキーボードだろうが」みたいな気持ちになりますよね。わたしは最初にこの話を聞いたときに「なにそれ、禅問答?哲学?頭大丈夫?ファイルはファイルだしキーボードはキーボードだろ」って思いました。
43 43
 
44 44
 「全てがファイル」とか言われると「世の中のすべてはファイルなのだ、そう、きみも、わたしも」みたいな禅問答をやられてるみたいな気持ちになるので、こういう言い方はあまりよくない感じがしますね。だったら、こんなふうに言われたらどうでしょうか? 「Linuxは、すべての入出力がファイルと同じ感じで扱えるような設計になっているんだよ」。つまり、プロセスが「ここでターミナルからの入力を受け取りたいんだけど」とか、「ネットワーク越しに入力もらってネットワーク越しに出力したいんだけど」みたいなことを言うと、OSさんが「はいよ、実際はHD(さいきんだとSSDかな)上のファイルじゃないんだけど、いつもファイルを通じてディスクを読み書きするのと同じやり方で扱えるように用意しといたよ!」みたいな感じでそのためのインターフェイスを用意してくれてるのです。
45 45
 
46  
-# 例:標準入出力
  46
+### 例:標準入出力
47 47
 
48 48
 さて、例を見てみましょうか。
49 49
 
@@ -104,15 +104,15 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」
104 104
 
105 105
 余談ですが、IO#readlinesは「ファイルの内容を全部読み込む」という挙動をしますが、では一行だけ読み込む IO#readline を使うとどういう挙動をするかなど、自分で確かめてみると、「あっブロックしてる」「あっ今読み込んでブロック中じゃなくなった」みたいなのがわかっておもしろいかもしれません。
106 106
 
107  
-# じゃあデフォルトじゃないのはなんなんだよ
  107
+### じゃあデフォルトじゃないのはなんなんだよ
108 108
 
109 109
 先ほどから標準入出力のデフォルトはどうこうみたいな話をしていますが、それはつまり標準入出力はその他の場所にもできるってことですね。そのための機能が「リダイレクト」と「パイプ」です。
110 110
 
111  
-# リダイレクト
  111
+### リダイレクト
112 112
 
113 113
 リダイレクトを使うと、標準入出力に別のファイルを指定することができます。ちなみに、シェル上(sh,bash,zshを想定)では、標準入力は「0」という数字、標準出力は「1」という数字、標準エラー出力は「2」という数字で表されます(なんでこんな謎っぽい数字使ってるのかは後で説明します)。出力系のリダイレクトは ">" という記号、あるいは">>"という記号で行えます。">"の場合、指定されたファイルがすでに存在する場合はそれを上書きします。">>"の場合、指定されたファイルがすでに存在する場合はファイルの末尾に追記します。
114 114
 
115  
-## 標準出力のリダイレクト
  115
+### 標準出力のリダイレクト
116 116
 例えば、
117 117
 
118 118
     # print_mew.rb
@@ -136,7 +136,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」
136 136
 
137 137
     $ ruby print_mew.rb > mew.txt
138 138
 
139  
-## 標準入力のリダイレクト
  139
+### 標準入力のリダイレクト
140 140
 
141 141
 当然、標準入力もリダイレクトすることが可能です。そのためには、"<"という記号を使います。
142 142
 
@@ -160,7 +160,7 @@ Ctrl+D を押すと、EOFというものが入力されます。この「EOF」
160 160
 
161 161
 stdin.rbの内容は標準入力を読み込んで標準出力にそのまま書き出すものなので、mew_copy.txtという新しいファイルに、mew.txtの内容、つまり「mew」 が書き込まれることになります。
162 162
 
163  
-## 標準エラー出力のリダイレクト
  163
+### 標準エラー出力のリダイレクト
164 164
 
165 165
 標準入出力について見てみたので、標準エラー出力についても見てみましょう。
166 166
 
@@ -184,7 +184,7 @@ stdin.rbの内容は標準入力を読み込んで標準出力にそのまま書
184 184
 
185 185
 &を付けることによって、「この1ってのは、1っていう名前のファイルじゃなくて標準出力を表す数字だよ!」ってことを言っているわけですね。さあ、またまた新しい疑問がわいてきました。なんで&付けるとそれがファイル名じゃなくて標準出力ってことになるの? そもそもなんで0とか1とか2とかって謎っぽい数字使ってるの? 疲れてきたので、そのあたりは次回にまわします。
186 186
 
187  
-# パイプ
  187
+### パイプ
188 188
 
189 189
 パイプについても簡単にみておきましょう。シェル上では、パイプは「|」という記号で実現されます。
190 190
 
@@ -194,6 +194,6 @@ stdin.rbの内容は標準入力を読み込んで標準出力にそのまま書
194 194
 
195 195
 また、パイプはシェル上でふたつのプロセスの標準入出力をつなぐだけではなく、プロセス上でも新しい入出力のペアを作ることができます。RubyだったらIO.pipeを使うと実現できるでしょう。Perlだったらpipe関数ですね。詳しくはrubyの公式リファレンスやperldoc,pipe(2)を参照してください。
196 196
 
197  
-## 次回予告
  197
+### 次回予告
198 198
 
199  
-次回はファイルの入出力について、もっと深くまで潜っていきますよ!ファイルディスクリプタの話をして、ソケットの話をします。そのあとようやくファイルディスクリプタとforkの話ができたらいいな!さーて、次回も、サービス!サービスゥ!
  199
+次回はファイルの入出力について、もっと深くまで潜っていきますよ!ファイルディスクリプタの話をして、ソケットの話をします。そのあとようやくファイルディスクリプタとforkの話ができたらいいな!さーて、次回も、サービス!サービスゥ!
22  004.md
Source Rendered
... ...
@@ -1,4 +1,4 @@
1  
-# ファイルディスクリプタ
  1
+## ファイルディスクリプタ
2 2
 
3 3
 さて、前回、プロセスがファイルを通じて外部との入出力する様を見て見ました。今回はさらにプロセスとファイル入出力について詳しく見てみましょう。
4 4
 
@@ -19,7 +19,7 @@
19 19
 
20 20
 * プロセスは、不要になった番号札をcloseというシステムコールでOSに返却します
21 21
 * OSは、番号札が返却されたので、「もうこれは使わないんだな」と判断して、ファイルを閉じます
22  
- 
  22
+
23 23
 と、こんな感じでファイルの入出力が行われているのですが、この「番号札」のことを、「ファイルディスクリプタ」と呼びます。実際、ファイルディスクリプタは整数値で表現されています。
24 24
 
25 25
 例を見てみましょう。今回もRubyを使います。
@@ -38,7 +38,7 @@
38 38
 
39 39
 「nyan.txtが書き込みモードで開かれたもの」についてる番号札が、5番なのが確認できましたね。
40 40
 
41  
-# 標準入出力のファイルディスクリプタ
  41
+### 標準入出力のファイルディスクリプタ
42 42
 
43 43
 さて、勘のいいひとはそろそろ例の標準入力は0、標準出力は1、標準エラー出力は2、という謎の数字の正体について、感付きつつあるのではないでしょうか。そうです。実は、「標準入力のファイルディスクリプタは0、標準出力のファイルディスクリプタは1、標準エラー出力のファイルディスクプタは2」なのです。実際に確かめてみましょう
44 44
 
@@ -51,7 +51,7 @@
51 51
 
52 52
 つまり、前回出てきた & という記号は、「ファイルパスじゃなくてファイルディスクリプタを指定してるぜ」という意味の記号だったわけですね!そして、なぜリダイレクトのときに標準入力や標準出力にあのような数字が使われているのかが理解できたと思います。
53 53
 
54  
-# オープンファイル記述
  54
+### オープンファイル記述
55 55
 
56 56
 さて、今はプロセスの側からがファイルディスクリプタをどう扱っているかについて見てみましたが、今度はOSの側から見てみましょう。
57 57
 
@@ -74,7 +74,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら
74 74
 ![ファイルディスクリプタの作成](https://raw.github.com/Shinpeim/process-book/master/images/004_01.png)
75 75
 ![ファイルへの書き込み](https://raw.github.com/Shinpeim/process-book/master/images/004_02.png)
76 76
 
77  
-# ファイルディスクリプタ/オープンファイル記述とfork
  77
+### ファイルディスクリプタ/オープンファイル記述とfork
78 78
 
79 79
 さて、では、forkしたとき、ファイルディスクリプタやオープンファイル記述はどうなるのでしょうか?
80 80
 
@@ -84,7 +84,7 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら
84 84
 
85 85
 そのため、forkしたときに同じ番号札(ファイルディスクリプタ)にたいして親プロセスと子プロセス両方で操作をすると、おかしなことになることがあります。
86 86
 
87  
-## オープンファイル記述は複製されない
  87
+### オープンファイル記述は複製されない
88 88
 例を見ましょう。
89 89
 
90 90
     # fork_fd.rb
@@ -126,14 +126,14 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら
126 126
     nyan nyan nyan nyan
127 127
     nyan nyan nyan nyan nyan
128 128
     nyan nyan nyan nyan nyan nyan
129  
-    
  129
+
130 130
 実行します
131 131
 
132 132
     $ ruby fork_fd.rb
133  
-    
  133
+
134 134
 さて、結果はどうなったでしょうか?オープンファイル記述が複製されていないことが実感できたかと思います。
135 135
 
136  
-## ファイルディスクリプタは複製される
  136
+### ファイルディスクリプタは複製される
137 137
 
138 138
 では今度は、ファイルディスクリプタは複製されているのを見てみましょう
139 139
 
@@ -166,11 +166,11 @@ OSのお仕事は、「プロセスからファイルの操作を頼まれたら
166 166
 ![forkされたときのイメージ](https://raw.github.com/Shinpeim/process-book/master/images/004_03.png)
167 167
 ![オープンファイル記述が共有されている](https://raw.github.com/Shinpeim/process-book/master/images/004_04.png)
168 168
 
169  
-## どうするのがベストプラクティスなの?
  169
+### どうするのがベストプラクティスなの?
170 170
 
171 171
 すでにfileがopenされている状態でforkすると、以上に見たように予期せぬ動作で混乱することがあります。そのため、forkした場合、親プロセスで使わないファイルは親プロセスですぐ閉じる、子プロセスで使わないファイルは子プロセスですぐ閉じるとすると、最も問題が起きにくいと思います。子プロセスでファイルを閉じたとしても、親プロセスでファイル使いたい場合に問題なく扱える(またはその逆も)のは、上に見た通りですからね
172 172
 
173 173
 
174  
-# 次回予告
  174
+### 次回予告
175 175
 
176 176
 ソケットの話してpreforkサーバーを自分で書いてみるつもり
18  005.md
Source Rendered
... ...
@@ -1,8 +1,8 @@
1  
-# preforkサーバーを作ってみよう
  1
+## preforkサーバーを作ってみよう
2 2
 
3 3
 さて、前回は、fork するとファイルディスクリプタ(以下fdと表記)は複製されるけどオープンファイル記述は共有されるというのを見ました。これを利用して、preforkモデルのサーバーを実際に書いてみましょう。
4 4
 
5  
-# tcp socketはファイルである
  5
+### tcp socketはファイルである
6 6
 
7 7
 以前見たとおり、Linuxではプロセスの入出力はファイルを通じて行います。とうぜん、ネットワークごしに入出力する tcp socket もファイルです。ここで「ファイルです」が意味するのは、プロセスがソケットを通じて入出力をしようと思えば、socket の fd を通じて write や read を行うということですね。では、実際に socket がファイルであるところを見てみましょう
8 8
 
@@ -20,7 +20,7 @@
20 20
 
21 21
 上記のような Ruby スクリプトを実行してみると、openしたソケットがfdを持つことが確認できるかと思います。
22 22
 
23  
-# クライアントの接続を受け入れる
  23
+### クライアントの接続を受け入れる
24 24
 
25 25
 今は socket を開いてなにもせずにすぐ閉じてしまいましたが、今度はクライアントの接続を受け入れてしてみましょう。
26 26
 
@@ -62,7 +62,7 @@
62 62
 
63 63
 今回はなにもせずに socket を close したので、クライアント側(telnetコマンドを打った側)ではすぐにサーバーからコネクションが切られてしまったわけですね。
64 64
 
65  
-# クライアントから送られてくるデータを読み込む
  65
+### クライアントから送られてくるデータを読み込む
66 66
 
67 67
 さっきはなにもせず socket を close してしまいましたが、今度はクライアントからデータが送られてきたらそれを読む、という動きにしてみましょう。
68 68
 
@@ -118,7 +118,7 @@
118 118
 
119 119
 いまは puts line で標準出力にクライアントからの入力を出力していますが、この行を socket に対する書き込みにすれば、いわゆるエコーサーバーとして動くプロセスになります。そのあたりは宿題とするので、自分で書き換えて動きを見てみてください。
120 120
 
121  
-# このサーバーは出来損ないだ、たべられないよ
  121
+### このサーバーは出来損ないだ、たべられないよ
122 122
 
123 123
 さて、これでクライアントから接続を待って、クライアントに接続されたらそのクライアントとネットワーク越しに入出力することができました。しかし、このサーバーには欠陥があります。わかりますか?
124 124
 
@@ -130,7 +130,7 @@
130 130
 
131 131
 をしてみると、先に telnet したほうは普通に動くのだけれど、もういっこのほうはいくら入力してもサーバープロセスがうんともすんとも言わないのが見て取れると思います。
132 132
 
133  
-# 明日の同じ時間にここに来てください。本当のサーバーってやつを見せてあげますよ
  133
+### 明日の同じ時間にここに来てください。本当のサーバーってやつを見せてあげますよ
134 134
 
135 135
 べつに用意する食材もないので、明日の同じ時間を待つ必要はありません。すぐにコードを書き換えてしまいましょう。
136 136
 
@@ -199,7 +199,7 @@ listening_socket はファイルでした。そのため、fd を持ちます。
199 199
 
200 200
 今までの話で、preforkサーバーが書けて、さらにどうしてそんなことが可能なのかも理解できましたね!
201 201
 
202  
-# preforkの利点、欠点
  202
+### preforkの利点、欠点
203 203
 
204 204
 さて、上に見たように、prefork サーバーはひとつのプロセスがひとつのクライアントを受け持つようなアーキテクチャになっています。このことは、いくつかの利点と欠点をもたらします。
205 205
 
@@ -207,6 +207,6 @@ listening_socket はファイルでした。そのため、fd を持ちます。
207 207
 
208 208
 一方で、アーキテクチャが単純なので、コードが追いやすいです。シンプルであることは、とても大切なことです。さらに、ひとつのクライアントがひとつのプロセスで処理されるためたとえばプロセスが暴走したとかそういうときの影響範囲がせまくなります。ひとつのプロセス内でたくさんのクライアントを受け持つと、そのプロセスがなんかおかしなことになったときに影響範囲が大きくなって困りますね。
209 209
 
210  
-# 次回予告
  210
+### 次回予告
211 211
 
212  
-次回はちょっと話を戻して、forkした際に親が先に死んだり終わったりしたらどうなるのとかそういう話をしたいなって思います。
  212
+次回はちょっと話を戻して、forkした際に親が先に死んだり終わったりしたらどうなるのとかそういう話をしたいなって思います。
14  006.md
Source Rendered
... ...
@@ -1,8 +1,8 @@
1  
-# ゾンビプロセスと孤児プロセス
  1
+## ゾンビプロセスと孤児プロセス
2 2
 
3 3
 さて、前回までで fork とかファイルとかのことはだいたいわかってきたかと思います。今回は、「親が死んだ子供は養子になるしかない」「子供が親の見てないところで死ぬとゾンビになってしまう」という話をします。
4 4
 
5  
-# 親が死んだ子供は養子になるしかない
  5
+### 親が死んだ子供は養子になるしかない
6 6
 
7 7
 今回はここ数日なにかと話題の Perl で行きましょう。
8 8
 
@@ -34,7 +34,7 @@
34 34
 
35 35
 これはどういうことかと言うと、親が死んだから、init さんのところに養子に入ったわけですね。親プロセスに先立たれて親の pid が存在しなくなったプロセスは、init が代わりに親プロセスとして振る舞ってくれます。
36 36
 
37  
-# 「子供が親の見てないところで死ぬとゾンビになってしまう」
  37
+### 「子供が親の見てないところで死ぬとゾンビになってしまう」
38 38
 
39 39
 「ゾンビプロセス」ってのを聞いたことあると思うんですけど、これはどういうプロセスなんでしょうか。コードで見てみましょう
40 40
 
@@ -55,7 +55,7 @@
55 55
         # 子プロセスは即死する
56 56
         exit;
57 57
     }
58  
-    
  58
+
59 59
 上記のようなスクリプトを zombie.pl として保存して、バックグラウンド実行してみましょう
60 60
 
61 61
     $ perl zombie.pl &
@@ -79,7 +79,7 @@
79 79
 
80 80
 さきほど、Ctrl+C で親プロセスを終了しましたが、この親は子を wait しないで死んでしまいました。ゾンビ状態だった子プロセスは、親プロセスが死んでしまったことにより、init さんの養子に入ります。すると、init さんが即座に wait でこのプロセスを看取ってくれて、無事にゾンビ状態だったプロセスが終了できたわけです。
81 81
 
82  
-# まとめ
  82
+### まとめ
83 83
 
84 84
 さて、このことから、以下のようなことがわかります。
85 85
 
@@ -88,6 +88,6 @@
88 88
 
89 89
 短いですが、今日はこんなところで。
90 90
 
91  
-# 次回予告
  91
+### 次回予告
92 92
 
93  
-次回はようやくシグナルについて書く予定です。共有メモリの話と、スレッドの話もその後にできればしたいけど、気力ないかもしれないので次回が最終回の可能性が微レ存……
  93
+次回はようやくシグナルについて書く予定です。共有メモリの話と、スレッドの話もその後にできればしたいけど、気力ないかもしれないので次回が最終回の可能性が微レ存……
22  007.md
Source Rendered
... ...
@@ -1,14 +1,14 @@
1  
-# シグナルとkill
  1
+## シグナルとkill
2 2
 
3 3
 さて、前回までで fork とプロセスの関係についてはなんとなく概要が把握できたんじゃないかなと思います。今回は、シグナルについてです。
4 4
 
5  
-# プロセスに外から影響を与えたい(シグナル編)
  5
+### プロセスに外から影響を与えたい(シグナル編)
6 6
 
7 7
 プロセスが外界とコミュニケーションを取るための方法として、ファイルディスクリプタを通じた入出力というものがあることは前回までで見てきたとおりです。じつは、プロセスが外界とコミュニケーションを取る方法としてもうひとつ、「シグナル」というものがあります。第二回で見たとおり、プロセスは生成されたあとは実行中となり、処理が終わるまでは一心不乱に決められた動きを行っています。しかしたとえば、無限ループに陥ってしまったプロセスなどは、外から「あっちょっと君、ストップ!ストップ!」という感じで止めてあげられる仕組みがないと困りますよね。そういう感じで、外からプロセスに対して「割り込み」を行うための仕組みが「シグナル」です。
8 8
 
9 9
 なにはともあれ、ためしてみましょう。
10 10
 
11  
-# シグナルを送ってみる
  11
+### シグナルを送ってみる
12 12
 
13 13
 まずはプロセスを作りましょう。
14 14
 
@@ -27,7 +27,7 @@ kill というのが、プロセスに対してシグナルを送るコマンド
27 27
 
28 28
 すると、さきほどまで存在していた perl プロセスが無くなっていることがわかると思います。これはいったいどうしたことでしょうか。実は、SIGINTというプロセスを受け取ると、デフォルト値ではそのプロセスは実行を停止するのです。sleep し続けていたプロセスに SIGINT というシグナルを送ったことによりプロセスに「割り込み」をして、そのプロセスの実行を止めてしまったわけですね。
29 29
 
30  
-# シグナルを受け取ったときの動作を変えてみる
  30
+### シグナルを受け取ったときの動作を変えてみる
31 31
 
32 32
 さきほど、「デフォルト値では」と言いましたが、ということは、シグナルを受け取ったときの動作を変更することだってできるわけです。やってみましょうか。
33 33
 
@@ -61,7 +61,7 @@ papas.pl という名前で上のようなスクリプトを作成して、バ
61 61
 
62 62
 これで無事にパパスは死にました。
63 63
 
64  
-# シグナルにはどんなものがあるの?
  64
+### シグナルにはどんなものがあるの?
65 65
 
66 66
 上に見たように、シグナルには SIGINT 以外にもいろいろないろいろなシグナルがあります。man 7 signal や man kill に一度目を通しておくと良いでしょう。それぞれのシグナルに、受け取ったときのデフォルトの動作が定義されています。
67 67
 
@@ -101,10 +101,10 @@ Signal のところがシグナルの名前、Value というところがその
101 101
     _人人人人人_
102 102
     > SIGPIPE <
103 103
      ̄YYYYY ̄
104  
-    
  104
+
105 105
 動かし続けることを前提としたプロセスでは、このあたりのシグナルをきちんとハンドリングしてあげないとハマることが多いので、頭の片隅に置いておくといいかもしれません。
106 106
 
107  
-# 次回へ続くなぞ
  107
+### 次回へ続くなぞ
108 108
 
109 109
 さて、シグナルについて基本的なことは見て来れたかと思います。では、forkなどと組み合わせて使った時にはどういう動きをするのでしょうか?見てみましょう。
110 110
 
@@ -143,7 +143,7 @@ OKですか? そうしたら、ここで再度 ps を実行してみましょ
143 143
       PID TTY      STAT   TIME COMMAND
144 144
     16753 pts/2    Ss     0:00 -bash
145 145
     17140 pts/2    R+     0:00  \_ ps f
146  
-    
  146
+
147 147
 子プロセスも一緒に消えていますね。では、今度は fg -> Ctrl+C のコンボではなく、kill -INT で SIGINT を送ってみましょう。
148 148
 
149 149
     $ perl fork_and_sleep.pl &
@@ -167,9 +167,9 @@ OKですか? そうしたら、ここで再度 ps を実行してみましょ
167 167
 
168 168
 さて、なぜこのようなことが起こるのでしょうか。この挙動を理解するには、「プロセスグループ」という新しい概念を学ぶ必要があります。
169 169
 
170  
-# というわけで次回予告
  170
+### というわけで次回予告
171 171
 
172 172
 次回はプロセスグループについて見てみましょう。多分次回が最終回!
173 173
 
174  
-# see also
175  
-[Perl Hackers Hub 第6回 UNIXプログラミングの勘所(3)](http://gihyo.jp/dev/serial/01/perl-hackers-hub/000603)
  174
+### see also
  175
+[Perl Hackers Hub 第6回 UNIXプログラミングの勘所(3)](http://gihyo.jp/dev/serial/01/perl-hackers-hub/000603)
32  008.md
Source Rendered
... ...
@@ -1,8 +1,8 @@
1  
-# プロセスグループ と フォアグランドプロセス
  1
+## プロセスグループ と フォアグランドプロセス
2 2
 
3 3
 前回はプロセスとシグナル、そしてシグナルを明示的にプロセスに送るためのコマンド kill について見ました。そして最後にひとつ謎が残ったわけですが、今回はその謎を解いて行きましょう。
4 4
 
5  
-# プロセスグループ
  5
+### プロセスグループ
6 6
 
7 7
 さて、じつは今まで一度も意識したことはありませんでしたが、プロセスというのはかならずひとつのプロセスグループというものに属します。見てみましょう。
8 8
 
@@ -29,7 +29,7 @@
29 29
 上記のような、 fork して sleep し続けるだけの fork.pl というスクリプトを作ってバックグラウンドで実行してみましょう。
30 30
 
31 31
     $ perl fork.pl &
32  
-    
  32
+
33 33
     $ ps o pid,pgid,command f
34 34
      PID  PGID COMMAND
35 35
     1620  1620 -bash
@@ -45,7 +45,7 @@
45 45
 
46 46
 ちなみに、プロセスグループにはリーダーが存在して、PGID と同じ数字の PID のプロセスが、プロセスグループのリーダーです。forkすると、同じグループに属する子分ができる、みたいな感じですね。
47 47
 
48  
-# プロセスグループをいじってみよう
  48
+### プロセスグループをいじってみよう
49 49
 
50 50
 さて、今かんたんに「fork すると子プロセスは自分と同じプロセスグループに属するようになる」と言いましたが、これはちょっとおかしいですね。そうです、以前見たように、すべてのプロセスは pid 1 のプロセスから fork で作られたのでした。そうなると、すべてのプロセスは pid 1 のプロセスと同じプロセスグループに属することになってしまいます。すべてのプロセスが同じグループに属すなら、グループの意味がないですね。だから、forkしたあと、プロセスグループをいじる仕組みが必要になってきます。それが setpgrp システムコールです。では例を見てみましょう。
51 51
 
@@ -106,13 +106,13 @@
106 106
 
107 107
 こういう感じで親プロセスのほうで引数付きで setpgrp を呼び出すことで、子プロセスの PGID を設定することもできます。
108 108
 
109  
-# いったんまとめ
  109
+### いったんまとめ
110 110
 
111 111
 こんな感じで、プロセスが fork で子プロセスを作ったとき、その時点ではその子プロセスは親プロセスと同じプロセスグループに属しています。プロセスグループを変更したいときには、この子プロセスの PGID を setpgrp システムコールでいじってあげれば良いわけですね。
112 112
 
113 113
 ちなみに、シェルから起動されたプロセスは、シェルが勝手に setpgrp を呼んでくれるので、それぞれがプロセスグループのリーダーとなっています。
114 114
 
115  
-# プロセスグループ全体に kill をしてみよう
  115
+### プロセスグループ全体に kill をしてみよう
116 116
 
117 117
 さて、いままでの話だけでは、「プロセスグループってのがあるのはわかったけど、そんなもんがあってなにがうれしいの」という感じがしますね。うれしいことのひとつとして、kill でプロセスグループに属する全てのプロセスに一気にシグナルを送れる、というものがあります。kill で pid を指定する部分に、"-" を付けてあげると、pid ではなくて pgid を指定したことになります。やってみましょう。
118 118
 
@@ -132,13 +132,13 @@
132 132
     1678  1678 -bash
133 133
     1702  1702  \_ ps o pid,pgid,command f
134 134
 
135  
-# 前回の謎に回答する
  135
+### 前回の謎に回答する
136 136
 
137 137
 ではここで、前回の謎に回答しましょう。前回謎だった挙動は、「fg でプロセスをフォアグラウンドにしてから Ctrl+C で SIGINT を送信したときは子プロセスごと殺されたのに、 kill -INT でバックグラウンドのプロセスに SIGINT を送信したら親プロセスだけが殺される」という挙動でしたね。
138 138
 
139 139
 勘のいいひとはすでにお気づきかもしれないですが、実は、「フォアグラウンド」とされる範囲は、プロセス単位ではなくて、プロセスグループ単位で決まっているのです。いくつか、例を見てみましょう。
140 140
 
141  
-    # fork_and_trap_sigint.pl 
  141
+    # fork_and_trap_sigint.pl
142 142
     use strict;
143 143
     use warnings;
144 144
 
@@ -149,12 +149,12 @@
149 149
     fork;
150 150
 
151 151
     sleep;
152  
-    
  152
+
153 153
 上記のようなスクリプトをフォアグランドで実行して、Ctrl+C でSIGINTを送ってみましょう。すると、"got SIGINT!" がふたつ標準エラーに出力されるはずです。これは、子プロセスと親プロセスが同じプロセスグループに属しているため、このふたつのプロセスがフォラグランドで実行されているからですね。
154 154
 
155 155
 では今度は、プロセスグループが別の場合を見てみましょう。
156 156
 
157  
-    # fork_and_setpgrp_and_trap_sigint.pl 
  157
+    # fork_and_setpgrp_and_trap_sigint.pl
158 158
     use strict;
159 159
     use warnings;
160 160
 
@@ -184,7 +184,7 @@
184 184
 
185 185
 別の例も見てみましょう。
186 186
 
187  
-    # read_stdin_in_child.pl 
  187
+    # read_stdin_in_child.pl
188 188
     use strict;
189 189
     use warnings;
190 190
 
@@ -197,7 +197,7 @@
197 197
 
198 198
         # 子の終了待つ
199 199
         waitpid($pid,0);
200  
-    } 
  200
+    }
201 201
     else {
202 202
         # STDINからの入力をエコーする
203 203
         while(my $line = <STDIN>) {
@@ -211,7 +211,7 @@
211 211
 
212 212
 ではこれをsetpgrpとの合わせ技でやるとどうなるでしょう?
213 213
 
214  
-    # setpgrp_and_read_stdin_in_child.pl 
  214
+    # setpgrp_and_read_stdin_in_child.pl
215 215
     use strict;
216 216
     use warnings;
217 217
 
@@ -224,14 +224,14 @@
224 224
 
225 225
         # 子の終了待つ
226 226
         waitpid($pid,0);
227  
-    } 
  227
+    }
228 228
     else {
229 229
         # setpgrpを呼び出して新しいプロセスグループのリーダーになる
230 230
         # これにより、子プロセスは親プロセスと異なるプロセスグループに
231 231
         # 属すことになり、フォアグランドで実行されている
232 232
         # プロセスグループから抜ける
233 233
         setpgrp;
234  
-        
  234
+
235 235
 
236 236
         # STDINからの入力をエコーする
237 237
         while(my $line = <STDIN>) {
@@ -243,7 +243,7 @@
243 243
 
244 244
 さて、これで前回謎だった挙動にも説明がつきましたね。これで、プロセスグループの解説はおしまいにします。
245 245
 
246  
-# おわりに
  246
+### おわりに
247 247
 
248 248
 これにてこのシリーズはおしまいです。いかがだったでしょうか? 一度プロセスまわりについてまとめておきたいという動機で書き始めたのですが、これを書きながらわたしも理解があやふやなところが洗い出せたりして、なにかと有意義でした。
249 249
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.