UNIX 6th code reading - プロセス管理

初めての方はこちらを参照 http://d.hatena.ne.jp/takahirox/20101024/1287923014

はじめに

今回は8章です。
プロセスの切り替えについてです。
割り込みなどもプロセス切り替えの一種ですが、9章の内容なので今回は扱いません。

また、今回の内容にはスワップ処理の絡んでくる箇所がありますが、スワップ処理を追うのはひとまず置いておきます。14章で詳細を確認するつもりです。

プロセスの切り替えかた

  • プロセスの切り替わるタイミング
    • 実行プロセスがsleepを呼び出し、自分自身を眠らせる。sleep内で呼ばれるswtchにより実行プロセスが切り替わる
    • 割り込みやトラップが発生したとき

後者は後の章で扱われるので、今回は前者の場合を考えます。

絵で表すとこんな感じです。起動時の流れを表したものの再掲です。

swtchはproc[ ]配列を探査し、以下の条件を満たしたプロセスに切り替えます。

  • p_statがSRUN(ready to run. 実行可能なプロセス)
  • 優先度が最も高い(p_priが最も小さい)

sleepはp_statをSSLEEPもしくはSWAITに設定します。sleep実行後にswtchを呼び出すことで自分以外の別のプロセスに切り替えることができます。

どういうときにsleepを呼び出すかについては後の章(クロックの章など)で明らかになると思われるので、今回は置いておきます。

優先度はsetpriという関数で時折再設定されます。上記からわかるように、setpriのアルゴリズムがプロセスの選択に大きく影響しています。

プロセスの切り替え自体はswtch内で呼ばれるretuによって行われます。
retuについては以前のエントリ参照。

スワッピング

実行するプロセスが扱うプログラムやデータはメインメモリ(core)に格納する必要があります。
しかし、メインメモリの容量は十分に大きくないので、全てのプロセスのプログラム・データを格納することはできません。
そこで、メインメモリの容量が足りなくなった場合、使用していないプログラム・データをディスクなどの領域(swap)に待避(swap out)しておきます。それを扱うプロセスが再活性したときにコアメモリに復活(swap in)させます。
このスワッピングという考えによって、限られたメインメモリをうまく使って多数のプロセスを実行することができます。

絵で表すとこんな感じです。


メモ

savu & retu & aretu

savuとretuは前回のエントリで書いたので省略。

aretuは引数で渡されたアドレスからr5, r6をロードする。PAR#7の切り替え(実行プロセスの切り替え)は行わない。swapからの復帰などに用いられる?

swtch

上述のように、実行プロセスを以下のプロセスに切り替えます。

  • p_statがSRUN(ready to run. 実行可能なプロセス)
  • 優先度が最も高い(p_priが最も小さい)

以下はソースコードに対するメモ

  • 2189 : 現在のプロセスのr5, r6を保存しておく
  • 2193 : proc#0(スケジューラ用プロセス)に切り替え
  • 2196 : 「現在のプロセスより優先度の高いプロセスが存在する」ことを示すrunrunフラグをリセット
  • 2203-2214 : proc[ ]配列を線形探査し、p_statがSRUNでp_flagのSLOADが0ではないプロセスのうち最も優先度の高い(p_priが最も低い)ものを選択する
  • 2218-2222 : もし実行可能なプロセスが存在しなかった場合、idle( )を呼び出して待つ
  • 2228-2229 : 選択したプロセスに切り替え
  • 2240-2243 : 選択したプロセスがスワップアウトされていたら? 詳細はスワップの章で追う予定。2238クラブに入るのはしばらくおあずけ
    • 2241 : SWAPのフラグをリセット
    • 2242 : r5, r6を復帰
  • 2247 : returnの先は2228もしくは2242行で復帰したr5, r6で決まる
    • swtchを呼び出した命令の次に戻る。なのでswtchを呼び出すことで中断されていたプロセスが再開される
setpri

各プロセスの優先度を決定する。優先度決定式は329P参照。
p_priの値が小さいほど優先度が高い。
使用時間についてはクロックの章で確認する予定。

  • 2165 : 再計算した優先度が、実行中のプロセスの優先度より低いならばrunrunをインクリメント
    • runrunは実行中のプロセスよりも「高い優先度のプロセス」が存在することを示すフラグ。再スケジュールを促す
    • しかし、ここでは「低い」ならばrunrunをインクリメントしている。Lions本では329Pで思わせぶりなことが書かれているが……
    • やっぱりバグっぽい?
sleep

実行中のプロセス自分自身を眠らせる関数。sleepの最後でswtchを呼び出し別のプロセスに切り替える。
資源の確保待ちだったり、CPUのタイムアウトだったりでそれ以上処理を進められなくなったときにsleepを呼び出し。別のプロセスの処理を進める、のだと思う。

sleepに渡されるpriが0以上ならば、signalが発生したとき処理を行う。0未満ならばsignalを無視する。

  • 2077, 2090 : priが0以上ならばp_statをSWAIT, 0未満ならばp_statをSSLEEPに設定する。これらstatの違いは?
  • 2084, 2093 : p_statを設定して自分自身を眠らせた後にswtchを呼び出して実行プロセスを切り替える。swtchではp_stat=SRUNのプロセスが選択されるので、たった今眠らせた自分が再活性することはない。
  • 2080-2083 : runinフラグが立っていたら(別のプロセスがスワップイン待ちをしていたら?)、それを起こす

時が経ち、眠らせたプロセスが起こされてswtchで選択されたときの話は以下。

  • 2085-2086 : 優先度が負だった場合、起きたタイミングでもsignalの処理を行う
  • 2095 : 2070行目で保存していたPSを復帰
  • 2096 : sleepを呼び出した先へ戻ってプロセスの処理続行
wakeup

proc[ ]を線形探査し、引数で渡されたchanで寝ているプロセスを全て起こす。

  • 2123 : setrunを呼び出すことでプロセスを起こす
setrun

プロセスのp_statをSRUNにする。SRUNになることで、swtchの選択対象となる。

  • 2141-2142 : 起こすプロセスの優先度が実行中のプロセスよりも高ければrunrunフラグをインクリメントする
  • 2143-2146 : runoutフラグが立っていて、起こすプロセスがスワップ領域にあるならば(p_flagのSLOADが0)runoutで寝ていたプロセスを起こす
    • runoutフラグって何を示す?
    • 2145 : setrun(&proc[0]);と同じ意味。スワップ処理しろ、ということ?
expand

プロセスのデータ+スタック領域のサイズ変更を行う。どういうときに呼ばれる?

  • 2277-2280 : サイズを小さくするなら、余分なメモリ領域をmfreeで開放するだけ
  • 2282 : そうでないなら、新たにmallocでメモリ領域確保を試みる
  • 2283-2289 : (メモリが足らずに)領域確保できなかった場合の処理
    • 2284 : swap用のr5, r6保存領域(?)にr5, r6を退避。
    • 2285 : 実行中のプロセスをスワップアウト。使用していたメインメモリ領域を開放
    • 2286 : 実行中のプロセスのp_flagをSSWAPにセット
    • 2287 : swtchを呼び出して、(メモリ領域が空くまで?)別のプロセスに切り替え。再活性したときのswtchのreturnでは、ここに戻らずにexpandを呼び出した場所へ戻る
  • 2290-2295 : mallocでメモリ領域を確保できた場合の処理
    • 2290 : 新しく確保したメモリ領域に、実行中プロセスのアドレスを変更する
    • 2291-2292 : セグメントを新しいメモリ領域にコピー
    • 2293 : 古いメモリ領域を開放
    • 2294-2295 : 新しく取得したメモリ領域を使うように、APR#7, r5, r6をセットし直す

その他メモ

  • スワッピングのオーバーヘッドはどれくらいなんだろう。スワッピングが多いとそれだけ実行速度が遅くなるわけで
  • runrunという名前の由来ってなんだろう
  • p_priが小さいほど優先度が高くなるような設計にしたのはなぜ?
  • それぞれの関数が、どこから呼ばれるのかも確認したい
  • SWAITとSSLEEPの違いは?

終わりに

プロセスを扱うセクションを読み終えることができました。ここまで来ると、OSの内部処理がだいぶ理解できてきて楽しいですね。

Lions本読書会後に色々追記するかもしれません。