プロセスとスレッド - Linux Kernel

研究室内で行われている、Linux Kernel勉強会で発表を行いました。勉強会でスピーカーを行うのは初めてでしたので、なかなか本番はスムーズに進まなくて残念でしたが、その一方で非常に良い経験となるものでした。
そして今回、この資料を眠らせておくのは勿体ない、ということでこれを当ブログにて公開したいと思います。
なお、オライリー・ジャパンより発行されている「詳解Linuxカーネル 第三版」を元にしています。今回担当したのは3章の前半である3.1「プロセス、軽量プロセス、スレッド」と3.2「ファイルディスクリプタ」です。
スライド配布ページ

でも、

ただ単にスライドのファイルを置いておくのもねということで、以下に今回の内容(3章)のさわりの部分について書いていきたいと思います。

まず始めに

プロセスとスレッドについて、ユーザ側、つまりシステム利用者(アプリケーションプログラマなど)から見たこれらの違いについて説明します。
そして、そのプロセスとスレッドの本質について、Kernel側から見たそれぞれの違いや共通点、そしてその扱いについて説明したいと思います。
流れとしては

  • プロセスについて
  • スレッドについて
  • スレッドはKernelの中でどう扱われている?

というものになります。

プロセス

プロセスっていう言葉は良く耳にしますが、具体的にこれは何でしょう?意外と答えるのが難しいのでは無いでしょうか。
これは色んな意味で使われます。"CPU時間を割り振る単位"だったり、"プログラムが実行している状態"だったり。どれも正解ですが、プロセスは「実体」として理解するのが良いでしょう。インスタンス、というと分かり易いかもしれません。
プログラムを実行させるとき、どうやって扱おうと困りますよね。メモリのアドレスもありますし、どこまで処理が進んだのか、という状態もありますし、どんなファイルを開いているのかという状態も有ります。これらを全て包んだものがプロセスです。なお、プロセスはタスクとも呼ばれますが、タスクの方が概念的には範囲が広く、後述するスレッドの意味でも使われる場合があるので注意して下さい。

プロセスの特徴

基本的に、一つのプログラムを実行すると、一つのプロセスが走ります。psコマンドを実行すると現在走っているプログラム、つまりプロセスが表示されます。

[m-bird@webserver ~]ssh$ ps
  PID  TT  STAT      TIME COMMAND
53454  p0  Is+J   0:00.02 -tcsh (tcsh)
27385  p1  Is+J   0:00.06 /bin/tcsh
28024  p2  Is+J   0:00.03 -tcsh (tcsh)
28199  p5  Ss+J   0:00.18 /bin/tcsh
76037  p6  Is+J   0:00.02 -tcsh (tcsh)
57122  p7  SsJ    0:00.03 /bin/tcsh
77533  p7  R+J    0:00.00 ps
76509  p8  IsJ    0:00.02 -tcsh (tcsh)
76519  p8  S+J    0:00.01 screen -r

ここで、プログラムを実行します。そして、プログラムを実行したまま別のターミナルで確認すると...

$ vim hoge

[m-bird@webserver ~]ssh$ ps
  PID  TT  STAT      TIME COMMAND
53454  p0  Is+J   0:00.02 -tcsh (tcsh)
27385  p1  Is+J   0:00.06 /bin/tcsh
28024  p2  Is+J   0:00.03 -tcsh (tcsh)
28199  p5  SsJ    0:00.18 /bin/tcsh
77534  p5  S+J    0:00.04 vim hoge      <= 新しくプロセスが!
76037  p6  Is+J   0:00.02 -tcsh (tcsh)
57122  p7  SsJ    0:00.03 /bin/tcsh
77535  p7  R+J    0:00.00 ps
76509  p8  IsJ    0:00.02 -tcsh (tcsh)
76519  p8  S+J    0:00.01 screen -r

新しくプロセスが作られている事が分かります。

ここで知っておいてほしいのは、1プロセッサには同時に1プロセスしか走らない、という事です。でも、psコマンドの結果は複数のプロセスが同時に走っているように見えますよね?
これは、あるプロセスがCPUにある程度実行してもらったら、他のプロセスにCPUを譲っているからです。これが短い間に行われるので、あたかも複数のプログラムが同時に走っているように見えるのです。
また、このプロセスにどれだけの時間を割り当てるかや、そのプロセスの優先度を決めるのがスケジューラと呼ばれるものです。
そして、プロセスには状態(ステート)があります。現在処理中であるRunning状態だったり、実行可能状態であるReadyであったり。この辺りについてはwikipediaのプロセス項目や、神奈川大学の資料が分かり易いかもしれません。

スレッドとは

それでは、スレッドとはどのようなものでしょう。これは、プロセスを細かくした実行単位と言えます。
一つのプログラムの中で、一つのプロセスの中で平行に処理を行いたい場合は、このスレッドというものを利用します。
あるアプリケーションを考えてみましょう。このアプリケーションは、入力を受け取って処理・出力します。このとき、入力を受け取ったり出力したり、というのを同時に行えると効率的です。なので、入力を受け取る処理と、その入力をリアルタイムで処理・出力する処理をそれぞれ別のスレッドとして実行するのです。

もう少し具体的に考えてみましょう。

ではもう一つの例を考えてみましょう。3Dゲームがあるとします。
3Dはポリゴンを計算したり、影を計算したり。物理シミュレーションもしなくてはならないでしょう。そして、これらは同時に計算しなくてはなりません。
もしスレッドという考えが無いとどうなるでしょう。ポリゴンを作ったら、それを物理シミュレーションして、その結果を元に影を計算する、というのを順番に行っていかなければなりません。
これがキャラクターが動くものだったとしたら、ある計算を少し行ったら次の計算を行う。それが終わったら次の計算をして、、、と考えるのも大変ですね。
ここで、スレッドがあった場合。れぞれの処理にスレッドを割り当てて、平行に動かしてしまえば良いのです。凄くシンプルですね。

スレッドはカーネルの中でどう扱われている?

少しおさらい

ここで少し整理しましょう。スレッドとプロセスについて、ざっくりと書くと以下の通りとなります。

  1. プロセス
    1. 1プロセッサで同時に動くのは1プロセス
    2. 時間単位で区切られる
  2. スレッド
    1. ロセスを更に細かくした概念
    2. プロセスの中で並行に動かしたいものをスレッドという単位で分ける
本題。

時々聞きますよね、「マルチプロセッサ、マルチコアの計算機だとマルチスレッドアプリケーションの方が早い」と。
しかし、鋭い人は上を読んで気づくでしょう。プロセッサ一つに同時に動くのは1プロセスだから、プロセスの中にスレッドを複数作ったとしても速度向上は望めないんじゃないか、と。
事実、古いLinux Kernelにおいては、マルチスレッドアプリケーションであろうとなかろうと、一つのプロセスとして扱われていました。しかし、これは非常に効率の悪いものとして、新しい仕組みが導入されました。それが「ライトウェイトプロセス(軽量プロセス)」です。
これは、複数のライトウェイトプロセスとの間でメモリアドレス空間やオープンしているファイル等、つまり資源を共有することにより、生成やプロセスの切り替えを早くしたものです。
これをスレッドに割り当てることにより、スレッドを(Kernelからは)プロセスとして扱えるようになりました。これにより、沢山のスレッドを持ったプロセスを、複数のコアの上で効率的に処理することが可能となりました。

想像してみよう

実際に、百数十コアというシステムがありますが、この上でシステムを走らすと想像してみましょう。
ここで、ある非常に複雑なプロセスを実行します。これはマルチスレッドで大量の処理を同時に演算・処理するプログラムとなっています。
もし、これが古いLinux Kernelの実装となっていたら。幾つもの重い処理を実行するスレッドが1つのプロセスの中にあったとしても、それはマルチスレッドでない他のプロセスと同じ扱いをされてしまします。つまり、1プロセスであるため1プロセッサの上で処理されるのです。
これだとどれだけ沢山のコア(プロセッサ)を積んだシステムだとしても、このままだと1つのコアの上で凄い重たいプログラムが走るだけで残りのコアはお休み状態です。非常に効率が悪いです。
一方、これが新しいLinux Kernelだったら。プロセスの中のスレッドは、それぞれライトウェイトプロセスとして扱われ、それぞれのライトウェイとプロセスは有り余るコアの上で走り回ることができます。そうすれば、1つのコアで処理が実行するよりも非常に効率よくプログラムが実行できることは簡単に分かると思います。

と言うわけで

  • ユーザから見たら
    • 1プログラムは1プロセスとして実行される
    • 1プロセスの中で複数の処理を並列に行いたいときは、一つの処理をスレッドという単位にして複数のスレッドを動かす
  • Kernelから見たら
    • 1プログラムは1プロセスとして扱う
    • 1プロセスの中でスレッドが複数あったら、それらをライトウェイトプロセスとして扱い、プロセスと同様に処理する。

以上、簡単にプロセスとスレッドの違い、そしてこれのシステム内(Kernel)での扱いについてでした。
内容について、もし間違いなどが有りましたらお気軽にご指摘下さい。

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版