読者です 読者をやめる 読者になる 読者になる

Linuxにおけるプロセス/スレッドの調査とか学習とか

概念的・理論的な意味におけるプロセスとスレッド

プロセスとはプログラムコードやメモリ空間などを含めた実行処理のインスタンスである
スレッドとはプロセス内でメモリを共有し、並行化するためのより細かい単位である

Linuxにおけるプロセスとスレッド

プロセスは概念的なプロセスをそのまま実装されたもの
プロセス内で並列・並行化するためのスレッドは2つの意味がある

  • 軽量プロセス(LWP)
    プロセス内でスレッドを実現するが、スレッドの実行スケジュールはOSのスケジューラが行う なので、OSスケジューラから見たら、プロセスとスレッド(LWP)は同じ単位になる
    OSのスケジューラを使うので、マルチコアにおけるCPU資源の割り当てが可能

  • ユーザースレッド(アプリケーションスレッド)
    ユーザ空間で 実装 されたスレッド機構
    実際は プロセス - LWP - ユーザースレッド という紐付けになる
    ユーザ空間で実装されたランタイムでスケジューリングされるので、プロセス内でのタスク切り替えになるのでシングルコアでしか動かない

なので、 Linux上でスレッドを使って並行・並列化するという文脈の時は

  • LWPを多重化すれば、並列化(CPUコアでスケールする)
  • ユーザースレッドを多重化すれば並行化(シングルコアでタイムスライス)

ユーザー空間上の実装で並列・並行化可能という意味で、この2つの方法を区分けせずに ユーザー(空間上の)スレッド と表記するケースがある

プロセススケジューラについて

プロセススケジューラを呼び出されるタイミングは

  • CPUによる200msごとの割り込みがあった場合
  • 実行中のスレッドが明示的にスケジューラを呼び出した場合
  • 他にもあるっぽい

これらのタイミングでプロセススケジューラが呼び出されて、その時に一番優先順にが高いプロセスにタイムスライスが割り当てられる
スケジューラはCPU割り当てのスイッチも行うことから、カーネルモードで実行される

カーネルモードとユーザーモードについて(CPUモード)

CPUのアーキテクチャ(x86とか)で実装されている実行権限コントロールの仕組み

カーネルモード = 特権モード = マスターモード = スーパーバイザーモード = システムモード
と状況によって呼ばれ方が違う

システムコールなどを通じてデバイスアクセスしたりする時はカーネルによってスレッドをカーネルモードに切り替える。
この切替が行われないままのユーザーモードだとデバイス操作などに必要なカーネルメモリ空間へのアクセスはできない(例外が発生する)

カーネルモードのユーザープロセス(スレッド) のことを カーネルスレッド を表記する場合があるが、状況によっては正解、、、?
カーネルスレッドはカーネルメモリ空間で実行される = カーネルモードのCPUで処理されているスレッドはカーネルメモリ空間へアクセスできる と同意っぽい

カーネル空間/ユーザー空間について

メモリ上においてカーネルモードのプロセスのみがアクセスできるメモリ空間

カーネルスレッドについて

PIDが0のinitや割り込み処理などを行うスレッド
ユーザープロセス(スレッド)と同じようにスケジューラによってCPU割り当てされる
カーネルスレッドはメモリ空間を固定で割り当てられていない
そのためスケジューラによって切り替わる直前のユーザープロセスのユーザーメモリ空間を使う(そのためカーネルスレッドのタイムスライスの時は毎回メモリ空間が異なる)

カーネルスレッドという言葉を使う時に OSカーネルの処理を専門で扱うスレッド を指す場合と カーネルモードのユーザープロセス(スレッド) を指す場合があるので注意(ここでは前者の意味)

プロセスと軽量プロセス(LWP)とスレッドと

プロセスはプログラムコードやスタック領域などを含めた実行状態のインスタンスみたいなもの
プロセス毎に違うメモリ空間が割り当てられているため、お互いのプロセスのメモリ空間には通常アクセスできない(そのため不具合があってもプロセス間で伝搬しない)

スケジューラーによってプロセスを切り替える時に実行中のコンテキスト(スタックなど)を一度保存したあと、切り替え後のプロセスも保存してあったコンテキストを復元して処理を続行する。
そのため切り替えるたびにこの処理が必要なため切り替えコストが高い(これをコンテキストスイッチが高い)と呼ぶ

スレッドはプロセスと違い、一つのメモリ空間を使って生成される。(プロセスというメモリ領域に複数のスレッドを生成することが可能)
そのため、上記のようなメモリを退避が不要なためコンテキストスイッチのコストが低い

Linuxではプロセスを生成するときにLWPとして生成することができる。
これは実質スレッドだが、スケジューラはプロセスとして扱うのでタイムスライスのコントロールはOSのスケジューラが行う

言語ごとのプロセスとスレッドの扱いについて

go(goroutine)やpython

- プロセス
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread
  ┃  └ユーザースレッド(アプリケーションスレッドと呼ぶ場合も): goroutine, python Thread

いわゆるグリーンスレッドを扱う言語
一つのプロセス(=LWP)上で、言語ランタイムによって擬似的にスレッドを生成する。 pythonや以前のRubyの場合はGIL(Global Interpreter Lock)という機構があるためパフォーマンスが出づらい(IO多重化には有効っぽい?)

pthreadやJVMのThreadの場合

- プロセス
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread
  ┃-LWP(ユーザー(空間上の)スレッド)
  ┃  └アプリケーションスレッド(ユーザースレッドと呼ぶ場合も): pthread, JVM Thread

いわゆるネイティブスレッドを扱う言語
LWPとアプリケーションスレッドが1:1なので、タスクスケジュールはOSの機構を使う

参考