Skip to content

食事する哲学者問題のシミュレーター

Notifications You must be signed in to change notification settings

kotto5/Philosophers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

93 Commits
 
 
 
 
 
 

Repository files navigation

哲学者たち

哲学者の数

引数 number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]

テーブルを囲むように哲学者がn人(n >= 1)座っている 哲学者は  1食べる  2考える  3寝る をのどれかを行っている。いずれかを同時にすることは出来ない

forks がテーブルに philosophers の人数だけある

哲学者は fork 2本を両手に持ちテーブルの真ん中にあるスパゲティを食べる

哲学者はご飯を食べたらfork を机に置き寝始める。起きたらまた考え始める。これらのsimulationは哲学者が餓死したタイミングで終わる。

全ての哲学者は食事が必要で、飢えてはいけない 哲学者は互いに会話しない 哲学者たちは誰が死にそうになっているかをしらない(優先度を動的に操作できない?) 哲学者は死なないようにする

気をつけたこと data race が起きないようにすること 処理速度を高めるためにオブジェクトを分割してmutex lock した 死に関する情報 フィロソファーの状態に関する情報 フォーク(リソース)

eat とは共有リソースを必要とするタスクの抽象化であり、 usleep() の部分がそのまま execve() に置き換わるイメージ

sleep は共有リソースを必要としない自己で完結するタスクの抽象化であり フィボナッチ数列 の項から数字を求めるプログラムなど、単に計算のみをする場合がそれである

共通の情報であっても、共有する必要がないもの( 引数のstatusなど ) に関しては、共有する意味が無いので、個プロセス(スレッド)が保持するようにした。

データを共有する意味とは、変更が共有できることにあり、変更を行わない情報は共有する意味がない。寧ろ、共有メモリ空間に置いてしまうと、どの情報が読み取り専用(data race が起きない)で、どの情報が書き込みされうるのかが曖昧になる。

よって、変更をする必要がないデータ(かつ サイズが大きく無いもの?)に関しては、それぞれのメンバに分けることにした。(status 構造体)

死に関する情報はmonitor が管理する 死とはプロセスの終了であり、今回の場合は、monitor が想定しているタスク終了時間よりも遅延しているケースのこと。

カーネルとプロセスみたいな関係を参考にしている 以下CHATGPTから参照

========== カーネルは、中々死なないプロセスに対していくつかの対処方法を持っています。以下にいくつかの例を挙げます。 ↓これ タイムアウト機能の利用:カーネルは、プロセスが特定の時間内に応答しない場合、プロセスを強制的に終了することができます。これは、プロセスが無限ループに陥った場合や、リソースを使い切ってしまった場合に有効です。

シグナルの送信:カーネルは、シグナルと呼ばれる通知をプロセスに送信することができます。これにより、プロセスに対して終了を促すことができます。例えば、SIGTERMシグナルを送信することで、プロセスに対して正常に終了するように促すことができます。 プロセスを強制終了する:最後の手段として、カーネルはプロセスを強制的に終了することができます。これは、プロセスがクラッシュしている場合や、システム全体に深刻な影響を与える可能性がある場合に有効です。 これらの対処方法を組み合わせることで、カーネルは中々死なないプロセスに対処することができます。しかし、これらの手段はプロセスがリソースを消費し続ける場合には効果が限定的であるため、適切な処置をとることが重要です。 =========

応答 というのが EATタスクを開始する場合のことであり、EATタスクが定期的に開始されない場合のケースを弾いている つまり、想像できるタスク内容は、共有リソースをEATタスクで取得し、自身のメモリ等に保存する そして、スリープタスクではEATタスクで保存したデータを解析し、何か成果物を作るのでは無いかと想定する。

この一定時間 というのが time_to_starve なのだとしたら、この時間はカーネルの都合の時間なのか、プロセスの都合なのか 解釈がふた通りできる。

カーネルの都合だと判断して作るなら、スレッドは基本的に何も考えずループするもので、カーネルの介入によって終了されるものということになる。 プロセスの都合(安全性の強化)だとするならば、プロセス自身が、自分が処理に苦しんでいる時に自滅する機能をつけるということになる。

どちらも、安全なプログラムを作る上で大切そう。 優先順位をつけて考えるならば、まずカーネルの機能の実装を優先するべきである。安全なプロセスを実行する時にはカーネルは無駄な仕事をすることになるかもしれないが、安全でないプロセスを実行する際の拡張性を考えれば、まずカーネル(オペレーター)が強い機能を持つべきである。

次に、安全なプログラムが必要である。しかしこの場合、カーネルと同じタイムチェックをする必要はない(寧ろプロセスだからこそできるのは内部の処理の遅延を確認することなので、全体統治をするカーネルよりも綿密なケアができる and それこそに価値があるはずである)

よって、EATタスクを実行したのを確認した時に、monitor が独自に time to dead を更新するような形にしようと思う。

(まあ一方で、今回の課題では 餓死する というシチュエーションであるから、プログラムが勝手に死ぬほうがシチュエーションにはあっているかもしれない)

整理として、共有リソースの抽象化として使っているmutex と 共有データの参照のために、data race を防ぐ役割の mutex が二つ存在する

fork だけは前者のものであるが、 t_wish t_dead の中にあるmutex は校舎のためのmutex である。

↑本当か?笑 mutex はぐぐると排他制御のための機構と出る。 ちょっと微妙。

max eat times も遣い手の都合であり 共有メモリを減らす一環としてMonitor の情報とした

update time to die を外したのは結構大きい決断 "monitor の都合"

自分で言っといてあれなんですが、自分か気にしていたのは とはちょっと違う気がしてきました

何度も作り直す中で、いくつか訂正しなければならない部分がいくつか見つかりました

================

1つ目の構造 monitor 無し philo だけ

行動時(sleep act think take a fork の時) 行動する直前で、フィロソファーが自分の最後に食事した時間を確認する 死んでたら行動を辞めて dead と出力する 偶数と奇数でfork の持つ順番を変えることでdead lock 回避

問題 ⇒1他のフィロが止まらない ⇒monitor 設置 ⇒2お腹が減っているphiloにfork が連続で行かなかったりする ⇒monitor で情報管理 ⇒3take a fork の時、mutex lock() の間に死ぬと 10ms 以上死に気付くのが遅れる⇒ ft_mutex_trylock()実装

2つ目の構造 monitor 追加

monitor は 常に誰か死んでいないか確認し、死んでいる人が居たら、"time x is dead "を出力しつつ それぞれのphiloとの共有メモリに終了しろと情報を残す

解決したこと(上の番号と対応) 1行動する直前で、monitor との共有メモリを確認してから行動する 終了しろ とあったら、終了する。 2自分がfork を取っていい人間なのか、monitor に許可を貰ってからfork を取り始める 今は、左右のphilo の中で一番お腹が減っている人がfork を取れるようになってます) (2人の時ちょっと入力によってはやばいかも) 3trylock で 10 ms の遅れは観測上起こらなくなった(環境依存ではありそう)

問題 1行動する直前に確認しても、行動するまでの間(コードでは一行とかの間)で死んでいる可能性がある ⇒その間にmonitor が死を発見して "x is dead"を出力する ⇒先ほど確認したphilo はそのまま "x is eating"などを出力してしまう ⇒死の後にlog が出てしまう ⇒出力の管理もmonitor に行ってもらう

===============

3つ目の構造

monitor がthread の生死の確認とlog の出力を担当し、philoは 共有メモリをlock するか、usleep()の仕事しかしないようにした

解決したこと

1 dead した後に何かのlog が出ることが100%防げるようになった 行動する時 1philo から共有メモリを使ってmonitor に行いたい行動と現在時刻を送り、許可を待たず行動する 2monitor は、philoのリクエストした時間の時点で、philoが送った時刻時点で死んでいなければlog を出力する 死んでいればdead を出力し、他の全員の行動を止める

許可を待たず行動してしまうので、dead のlog を出力した後もeat 等のusleep()が行われうる。 これは今回は問題ないと解釈しているのと、もし問題あるようならdeteach して、メインがreturn してしまえば解決できる

問題 1monitor がやることが増えたからか、重い ⇒writer thread を用意し、io 部分を分担する

==============

4つ目

writer と分担した

問題 1 めっちゃ重い!何故!

要求を伝える為のwish 構造体へのアクセスが集中していて重いのか?
⇒要素を分割して、同じmutex を常にロックしようとしないようにする?(未実装)

monitor thread 分けても、結局同期管理(死の後に絶対に出力しない)の為に時間かかりそう

About

食事する哲学者問題のシミュレーター

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published