|
次のページ
前のページ
目次へ
5. IPC機構本章ではLinux 2.4 カーネルで実装されている IPC 機構のセマフォ、共有メモリおよびメッセージキューについて記述します。 四つの節で構成され、最初の三つの節では セマフォと メッセージキュー、 共有メモリを順に取り上げて、インターフェースとサポート関数を説明します。また、 最後の節では、三つの機構で共有される共通関数群とデータ構造を説明します。 5.1 セマフォこの節で説明している関数はユーザレベルのセマフォ機構の実装になっています。この実装部分はカーネルスピンロックとカーネルセマフォの利用に頼っていることに注意しましょう。 混乱を避けるため、カーネルのセマフォを参照するときは「カーネルセマフォ」と呼ぶことにします。その他の単に「セマフォ」というときは、ユーザレベルのセマフォを指すことにします。 セマフォシステムコールのインターフェースsys_semget()全てのsys_semget()への呼出しは、カーネルグローバルな sem_ids.semカーネルセマフォで保護されます。 新しいセマフォのセットを作らなければならない場合は、 newary() 関数が呼ばれて、新しいセマフォセットの作成と初期化をします。そして新しいセットID が呼出し元に戻されます。 キーの値が既存のセマフォセットに対して発行されたものだった場合は、 ipc_findkey() が呼び出され、相当するセマフォディスクリプタ配列インデックスを見つけます。パラメータと呼出し元の パーミッションは、セマフォのセットIDを返却する前に確認されます。 sys_semctl()IPC_INFOと SEM_INFO そして SEM_STAT コマンドについては、必要な関数処理を行う semctl_nolock() を呼び出します。 GETALL、 GETVAL、 GETPID、 GETNCNT、 GETZCNT、 IPC_STAT、 SETVAL、 SETALL コマンドについては、 必要な関数処理を行う semctl_main() を呼び出します。 IPC_RMIDや IPC_SET コマンドについては、必要な関数処理を行う semctl_down() を呼び出します。 両方の演算の間は、グローバルな sem_ids.sem カーネルセマフォを保持します。 sys_semop()呼出し引数の有効性を確認した後、セマフォの操作データをユーザ空間から一時バッファへコピーします。 小さい一時バッファで大きさが十分であればスタックバッファが使われます。不十分なら、大きいバッファが割り当てられます。セマフォの操作データのコピーが終わった後に、グローバルなセマフォスピンロックをロックします。そしてユーザが指定したセマフォセットIDが確認されます。セマフォセットへのパーミッションも有効か確認されます。 ユーザが指定したセマフォ操作が全て解析されます。この処理の間、カウントは SEM_UNDO フラグセットをもつ全ての操作を保持します。
セマフォ値を減算する操作のときには、 もし、これらの操作のいずれかがセマフォの状態を変化させるのであれば、このセマフォセットに関連する undo 構造体が 現在のタスクの undo リストから探されます。この検索の間、もし undo 構造体の任意のセマフォセットIDが -1 であると分かったならば、 freeundos() が呼ばれ、 undo 構造体が解放されてリストから削除されます。もし、undo 構造体がこのセマフォセットについて見つからなければ、 alloc_undo() が呼ばれて割り当ておよび初期化が行われます。 一連の操作を実行するために
try_atomic_semop() 関数を 非ブロックセマフォ操作try_atomic_semop() 関数は、一連の全ての操作が成功したときに、ゼロを返します。この場合には、 update_queue() が呼び出され、セマフォセットのうち保留されているセマフォの操作のキューを走査して、もうブロックの必要がない休止タスクを起こします。この処理によって、この場合の sys_semop() システムコールの実行が完了します。 セマフォ操作の失敗もし、 try_atomic_semop()が負の値を返した場合、 障害状態に遭遇したことになります。この場合、操作は全て実行されません。 これは、セマフォ操作が、無効なセマフォ値になったり、IPC_NOWAITにマークづけられた操作が実行完了できなかった場合に起ります。エラー状態はその後、sys_semop()の呼出し元に返されます。 sys_memop()から戻る前に、セマフォセットの保留されたセマフォ操作のキューを走査するため update_queue() を呼出し、もうブロックの必要のない休止タスクを起こします。 ブロッキングセマフォ操作try_atomic_semop()関数は、セマフォ操作の一つがブロックされ、 一連のセマフォ操作が実行されなかった場合には 1 を返します。この場合、新しい sem_queueの要素がこれらのセマフォ操作を含めて初期化されます。もし、これらの操作のいずれかがセマフォの状態を変化させそうであれば、新しいキューの要素はキューの末尾に追加されます。 そうでなければ新しいキューの要素がキューの先頭に挿入されます。 現在のタスクの 起こされたとき、なぜ起こされたか、どのように反応すべきかを判断するため、タスクはグローバルセマフォロックを再ロックします。以下のような場合が処理されます。
セマフォ独自にサポートする構造体以下の構造体は、セマフォのサポートに特に使われます。 struct sem_array
struct sem
struct seminfo
struct semid64_ds
struct sem_queue
struct sembuf
struct sem_undo
セマフォサポート関数以下の関数は特にセマフォのサポートのために使われます。 newary()newary() は、新しいセマフォセットに必要となるメモリを割り当てるために、
ipc_alloc()を利用しています。そして、セマフォセット記述子とセット中の各セマフォの全てに十分なメモリを割り当てます。
割り当てられたメモリはクリアされ、セマフォセットディスクリプタの最初の要素のアドレスが、
ipc_addid() へと渡されます。
ipc_addid() は、新しいセマフォセット記述子のエントリ配列を予約し、データ(
struct kern_ipc_perm)を初期化します。
グローバルな
ipc_addid()の呼出しに続く全ての操作は、グローバルセマフォスピンロックを保持した状態で実行します。グローバルセマフォスピンロックをアンロックした後に、newary() は、(sem_buildid()経由で) ipc_buildid() を呼出します。この関数は、セマフォセットディスクリプタのインデックスを使用し、ユニークな ID を生成します。そして、newary() の呼出し元に戻ります。 freeary()freeary() は semctl_down()によって呼び出され、以下に列挙する機能を実行します。グローバルなセマフォスピンロックがロックされた状態で呼び出され、スピンロックが解除された状態で戻されます。
semctl_down()semctl_down() は、 semctl()システムコールの IPC_RMID と IPC_SET 操作を提供します。セマフォセットIDとパーミッションは、これらの操作に先立って確認され、どちらの場合も、操作の間はグローバルセマフォスピンロックを保持します。 IPC_RMIDIPC_RMID 操作では、セマフォセットの削除を行うため、 freeary()を呼び出します。 IPC_SETIPC_SET 操作では、セマフォセットの semctl_nolock()semctl_nolock() は sys_semctl() から呼び出され、IPC_INFO、SEM_INFO および SEM_STAT 機能を実行します。 IPC_INFO と SEM_INFOIPC_INFO と SEM_INFO は、臨時に
seminfo バッファを初期化し、変更されなかったセマフォの統計データを読み込ませます。そして、グローバルの SEM_STATSEM_STAT は、臨時に
semid64_dsバッファを初期化します。その後、グローバルセマフォスピンロックを保持し、 semctl_main()semctl_main() は、 sys_semctl() により呼び出され、以下の節で示している多くのサポートする機能を実行します。全ての操作に先だって、semctl_main() は、グローバルセマフォロックをロックし、セマフォセットIDとパーミッションの有効性を確認します。スピンロックは戻る前に解除されます。 GETALLGETALL 操作は、現在のセマフォ値を一時的なカーネルバッファへ読み込み、ユーザ空間へコピーします。もしセマフォバッファが小さければ、小さなスタックバッファが使われます。そうでなければ、スピンロックが一時的に落とされ、大きなバッファを確保します。セマフォ値を一時バッファへコピーする間、スピンロックを保持します。 SETALLSETALL 操作は、セマフォ値をユーザ空間からカーネルの一時バッファへコピーし、その後セマフォセットへコピーします。一時バッファへユーザ空間から値をコピーする間、および正しい値か確認する間、スピンロックを落とします。もしセマフォセットが小さければ、スタックバッファが使われ、そうでなければ大きなバッファが確保されます。以下の操作をセマフォセットへ行う間、スピンロックを再取得し、保持します。
IPC_STATIPC_STAT 操作では、 GETVALエラーのない場合、GETVALでは、システムコールの返り値に指定したセマフォの値が設定される。 GETPIDエラーのない場合、GETPIDでは、システムコールの返り値にセマフォでの最後の操作に対応する pid を設定します。 GETNCNTエラーのない場合、GETNCNT ではシステムコールの返り値に、セマフォが 0 未満になるのを待っているプロセスの数を設定します。この数値は count_semncnt() 関数が計算します。 GETZCNTエラーのない場合、GETZCNT ではシステムコールの返り値に、セマフォが0に設定されるのを待っているプロセスの数を設定します。この数値は count_semzcnt() 関数が計算します。 SETVAL新しいセマフォ値の有効性を確認したあと、以下の機能が実行されます。
count_semncnt()count_semncnt() は、セマフォ値が 0 より小さくなるのを待っているタスク数を数えます。 count_semzcnt()count_semzcnt() は、セマフォ値が 0 になるのを待っているタスク数を数えます。 update_queue()update_queue() は、セマフォセットの保留中 semops キューを走査して、
try_atomic_semop() を呼び出し、
どのセマフォ操作のシーケンスが成功しそうかを判断します。
もしキュー要素の状態により、ブロックされたタスクがすでに起こされていることが示されていたら、
そのときはそのキュー要素はスキップされます。
キューのその他の要素では、 もし一連の操作がブロックされるなら、update_queue() は一切変更されることなく戻ります。 もしセマフォ操作の一つが無効なセマフォ値を引き起こしたり、IPC_NOWAIT とマークされている操作が完了できなかったならば、一連の操作は失敗します。そのような場合は、セマフォ操作によりブロックされていたタスクは起こされ、キューの状態として適切なエラーコードが設定されます。キュー要素はもちろんキューから削除されます。 もし一連の操作が変更を生じないならば、
try_atomic_semop()
の undo パラメータとして 0 を渡します。
もしそれらの操作が成功すれば、処理は完了したと見なして、キューから削除します。ブロックされたタスクが起こされ、キュー要素の もしセットの操作がセマフォの値を変更する場合で、処理が成功できるなら、もうブロックされる必要のない休止タスクが起こされます。操作が実行されなくても、キュー要素がキューから削除されることはありません。セマフォ操作は、起こされたタスクによって実行されます。 try_atomic_semop()try_atomic_semop() は、 sys_semop() と update_queue() により呼び出され、一連のセマフォ操作が全て成功するかどうか判断します。これは、各操作の実行を試みることによって判断が行われます。 もしブロックされた操作に遭遇したなら、プロセスはアボートし、全ての操作は差し戻されます。 もしIPC_NOWAIT が設定されていたなら -EAGAIN が返されます。そうでなければ 1 が返され、一連のセマフォ操作がブロックされたことを示します。 もしセマフォ値がシステムの制限を越えて変更されるならば、全ての操作は差し戻され、 -ERANGE が返されます。 もしシーケンス中の全ての操作が成功しても、 sem_revalidate()sem_revalidate() は、グローバルセマフォスピンロックが一時的に落とされ、再度ロックしなければならないときに呼び出されます。 つまり、 semctl_main() と alloc_undo() によって呼び出されます。セマフォIDとパーミッションの有効性を確認し、良ければグローバルセマフォスピンロックをロックして戻ります。 freeundos()freeundos() は、プロセスの undo リストを走査し、要求された undo 構造体を探します。 もし見つかれば、undo 構造体はリストから削除され、解放されます。 そして、プロセスリストの次の undo 構造体へのポインタが返されます。 alloc_undo()alloc_undo() は、グローバルセマフォスピンロックを獲得して呼ばれることを期待しています。エラーがあった場合は、スピンロックを解除して戻ります。 グローバルセマフォスピンロックが解除され、 sem_undo構造体と、セット中の各セマフォの修正値の配列の両方に十分なメモリを確保するため、kmalloc()を呼び出します。 成功すれば、グローバルスピンロックが sem_revalidate()を呼び出すことで再確保されます。 新しい semundo 構造体が初期化され、この構造体のアドレスが呼出し元によって与えられたアドレスへ格納されます。新しい undo 構造体は、現在のタスクの undo リストの先頭へ位置させられます。 sem_exit()sem_exit() は、do_exit() により呼び出され、存在するタスクの全ての undo 修正を実行する責任があります。 もし現在のプロセスがセマフォでブロックされているなら、 グローバルセマフォスピンロックを保持して sem_queueリストから削除します。 そして現在のタスクのための undo リストが走査され、リストの各要素の処理毎にグローバルセマフォスピンロックの取得と解除が行われつつ、以下の操作が実行されます。以下の操作は、各 undo 要素ごとに実行されます。
リストの処理が完了したならば、current->semundo の値をクリアします。 5.2 メッセージキューメッセージシステムコールインターフェースsys_msgget()sys_msgget()へのコール全体は、グローバルメッセージキューセマフォ( msg_ids.sem)により保護されています。 新しいメッセージキューを作らなければならない場合、 newque()関数が 呼び出され、新しいメッセージキューの作成と初期化を行い、新しいキューIDを呼出し元に返します。 もし与えられたキーの値が既存のメッセージキューへなら、 ipc_findkey()が呼び出され、グローバルメッセージキューデスクリプタ配列(msg_ids.entries)の対応するインデックスを探します。呼出し元のパラメータとパーミッションは、メッセージキューIDを戻す前に確認されます。検索操作と確認は、グローバルメッセージキュースピンロック(msg_ids.ary)が保持された状態で行われます。 sys_msgctl()sys_msgctl()へ渡されるパラメータは、メッセージキューID( IPC_INFO (または MSG_INFO)グローバルメッセージキューの情報がユーザ空間にコピーされます。 IPC_STAT (または MSG_STAT)タイプ struct msqid64_ds の一時バッファが初期化され、グローバルメッセージキュースピンロックを取得します。 呼び出したプロセスのアクセス権を確認したら、メッセージキューIDに対応しているメッセージキューの情報を、一時バッファへ読み込みます。そしてメッセージキュースピンロックを解除します。一時バッファの内容は、ユーザ空間へ copy_msqid_to_user() により書き出します。 IPC_SETユーザデータは、 copy_msqid_to_user()によりコピーされます。グローバルメッセージキューセマフォとスピンロックはこのとき取得され、最後に解除されます。メッセージキューID と現在のプロセスのアクセス権の有効性が確認された後、メッセージキューの情報がユーザから与えられたデータで更新されます。その後、 expunge_all() と ss_wakeup() が呼び出され、全てのメッセージキューの受け手と送り手のウエイトキューで休止しているプロセスを起こします。これは、そのときには受け手が厳密なアクセス権により排除されていたり、送り手がキューサイズが増したことによりメッセージの送信が可能になる場合があるためです。 IPC_RMIDグローバルメッセージキューセマフォが取得され、グローバルメッセージキュースピンロックがロックされます。メッセージキューIDと現在のタスクのアクセス権の有効性が確認されたあと、 freeque()が呼び出されてメッセージキューIDに対応づけられているリソースを解放します。 そして、グローバルメッセージキューセマフォとスピンロックが解除されます。 sys_msgsnd()sys_msgsng() は、パラメータとしてメッセージキューID(
sys_msgrcv()sys_msgrcv() 関数は、パラメータとしてメッセージキューID(
メッセージ独自の構造体メッセージキューのデータ構造体は msg.c で定義されています。 struct msg_queue
struct msg_msg
struct msg_msgseg
struct msg_sender
struct msg_receiver
struct msqid64_ds
struct msqid_ds
msg_setbuf
メッセージサポート関数newque()newque() は、新しいメッセージキュー記述子( struct msg_queue)のためのメモリを割り当て、 ipc_addid() を呼び出して、新しいメッセージキュー記述子用にメッセージキュー配列のエントリを予約させます。 メッセージキュー記述子は以下のように初期化されます。
freeque()メッセージキューが削除されるときは、 freeque() 関数が呼ばれます。この関数は、グローバルメッセージキュースピンロックが、呼出し元関数によって既にロックされているものと仮定します。指定されるメッセージキューに関連している全てのカーネルリソースを解放します。まず、(msg_rmid経由で) ipc_rmid() を呼び出し、グローバルメッセージキュー記述子の配列からメッセージキュー記述子を削除します。そして、 expunge_all を呼び出して、全ての受け手を起こし、 ss_wakeup()を呼び出して、このメッセージキューで休止している全ての送り手を起こします。 最後に、グローバルメッセージキュースピンロックを解除します。このメッセージキューに格納されている全てのメッセージは解放され、メッセージキュー記述子のメモリも解放されます。 ss_wakeup()ss_wakeup() は与えられたメッセージ送り手ウエイトキューで待ちになっている全てのタスクを起こします。 この関数が freeque()から呼ばれたなら、キューにある全ての送り手はキューから削除されます。 ss_add()ss_add() はパラメータとしてメッセージキュー記述子とメッセージ送り手データ構造体を受け取ります。現在のプロセスをもとにメッセージ送り手データ構造体の ss_del()与えられたメッセージ送り手データ構造体( expunge_all()expunge_all() は、パラメータとしてメッセージキュー記述子( load_msg()プロセスがメッセージを送るとき、 sys_msgsnd() 関数は、最初に load_msg() 関数を呼び出し、メッセージをユーザ空間からカーネル空間へと読み込みます。 メッセージはカーネルメモリ内ではデータブロックのリンクリストとして表現されています。 最初のデータブロックに関連しているのは、全てのメッセージを表す msg_msg 構造体です。msg_msg 構造体に関連づけられているデータブロックは、DATA_MSG_LEN のサイズに制限されています。 データブロックと構造体はメモリで一ページで取りうる最大の一つの連続したメモリブロックに割り当てられています。もし全てのメッセージが最初のデータブロックに入りきらなければ、追加のデータブロックが割り当てられ、リンクリストに登録されます。これら追加のデータブロックは、DATA_SEG_LEN にサイズが制限されており、各々は関連する msg_msgseg) 構造体を読み込みます。msg_msgseg 構造体と対応するデータブロックはメモリで一ページで取りうる最大の一つの連続したメモリブロックに割り当てられています。この関数は、成功時には新しい msg_msg 構造体のアドレスを返り値にもって戻ります。 store_msg()store_msg() 関数は、 sys_msgrcv() により呼び出され、受けたメッセージを呼出し元の提供するユーザ空間バッファへ再度組み立てます。 msg_msg 構造体と msg_msgseg構造体により示されるデータは、ユーザ空間バッファへ順次コピーされます。 free_msg()free_msg() 関数はメッセージデータ構造体 msg_msg とメッセージセグメントのメモリを解放します。 convert_mode()convert_mode() は
sys_msgrcv() から呼び出されます。パラメータとして、指定するメッセージタイプのポインタ( testmsg()testmsg() 関数は、メッセージが受け手の指定する条件に合致するか調べます。もし、以下の条件の一つが真となれば、1を返します。
pipelined_send()プロセスは pipelined_send() を使うことで、対応づけられているメッセージキューへ入れるのではなく、直接待機中の受け手へメッセージを送ることができます。
与えられたメッセージを待つ最初の受け手を見つけるため、
testmsg() 関数を呼び出します。もし見つかれば、待っている受け手は受け手ウエイトキューから削除され、関連する受け手のタスクは起こされます。メッセージは受け手の 受け手を探すプロセスでは、与えられたメッセージに対して小さすぎるサイズを要求していることで、潜在的受け手が分かるでしょう。そのような受け手はキューから削除され、起こされてエラー状態 E2BIG を渡されます。このエラーは、 copy_msqid_to_user()copy_msqid_to_user() はカーネルバッファの内容をユーザバッファにコピーします。パラメータとしてユーザバッファと msqid64_ds 型のカーネルバッファと、新しいIPC バージョンか古い IPC バージョンかを示すバージョンフラグを受け取ります。 もしバージョンフラグが IPC_64 ならば、 copy_to_user() が起動され、カーネルバッファからユーザバッファへ直接コピーします。そうでなければ、struct msqid_ds 型の一時バッファが初期化され、カーネルデータはこの一時バッファへ変換されます。そして、copy_to_user() が呼び出され、一時バッファの内容がユーザバッファへコピーされます。 copy_msqid_from_user()copy_msqid_from_user() 関数は、パラメータとしてstruct msq_setbuf型のカーネルメッセージバッファとユーザバッファと、新しいIPC バージョンか古い IPC バージョンかを示すバージョンフラグを受け取ります。IPCバージョンが新しい場合、copy_from_user()が呼び出されて、ユーザバッファの内容を
msqid64_ds 型の一時バッファへコピーします。
そしてカーネルバッファの 5.3 共有メモリ共有メモリシステムコールインターフェースsys_shmget()sys_shmget() は、呼出し全体をグローバル共有メモリセマフォにより保護します。 新しい共有メモリセグメントを作る必要のある場合、 関数 newseg() が呼び出され、新しい共有メモリセグメントを作成し初期化します。新しいセグメントのIDが呼出し元へ返されます。 既存の共有メモリセグメントの キー値が与えられている場合、共有メモリ記述子配列の対応するインデックスを見つけ出し、パラメータと呼出し元のパーミッションを確認後、共有メモリセグメントIDを返します。 検索操作と有効性の確認はグローバル共有メモリスピンロックを保持した状態で行われます。 sys_shmctl()IPC_INFO一時バッファ shminfo64 が システムワイドの共有メモリパラメータとともに読み込まれ、呼出し元のアプリケーションからアクセスするユーザ空間へコピーします。 SHM_INFO共有メモリのシステムワイドの統計情報を集める間は、グローバル共有メモリセマフォとグローバル共有メモリスピンロックを取得します。
関数
shm_get_stat() を呼び出し、メモリ中に存在する共有メモリページ数とスワップアウトした共有メモリページ数を計算します。
SHM_STAT, IPC_STATSHM_STAT と IPC_STAT のため、 struct shmid64_ds 型の一時バッファが初期化され、グローバル共有メモリスピンロックをロックします。 SHM_STAT の場合は、共有メモリセグメント ID パラメータは、1次元のインデックス(つまり、0 から n で、n はシステムの共有メモリ ID の番号) を期待しています。 インデックスの有効性を確認後、 ipc_buildid() が(shm_buildid()経由で)呼び出され、インデックスを共有メモリ ID へ変換します。SHM_STAT を渡した場合、共有メモリ ID が返り値になります。この機能は文献にないけれども、ipcs(8) プログラムのために準備されていることに注意しましょう。 IPC_STAT の場合、共有メモリセグメント ID パラメータについて、IDが shmget() を呼出して生成されていることを期待します。 この ID は処理を行う前に有効性が確認されます。IPC_STAT を渡した場合、0 が返り値となります。 SHM_STATと IPC_STAT の双方について、呼出し元のアクセス権の有効性を確認します。要求された統計情報は一時バッファへ読み込まれ、その後呼出し元へコピーされます。 SHM_LOCK, SHM_UNLOCKアクセス権の有効性を確認後、グローバル共有メモリスピンロックをロックし、共有メモリセグメントIDの有効性を確認します。SHM_LOCK と SHM_UNLOCK 双方で、その処理を行うため、 shmem_lock() を呼び出します。 shmem_lock()のパラメータには、処理すべき関数の識別子を渡します。 IPC_RMIDIPC_RMID の処理の間は、グローバル共有メモリセマフォとグローバル共有メモリスピンロックが取得されます。共有メモリID の有効性を確認したあと、もし current attachment がなければ、 shm_destroy() を呼出し、共有メモリセグメントを破壊します。そうでなければ、SHM_DEST フラグをセットし、これが破壊するという印になります。そして、他のプロセスを共有メモリIDの参照から守るために、IPC_PRIVATE フラグをセットします。 IPC_SET共有メモリセグメントIDとユーザのアクセス権の有効性を確認後、共有メモリセグメントの sys_shmat()sys_shmat() は、共有メモリセグメントIDと共有メモリセグメントを配置するアドレス( もし 呼出し元のアクセス権の有効性が確認され、共有メモリセグメントの 共有メモリセグメントページへの仮想メモリマップを作るため、 do_mmap() 関数を呼び出します。これはカレントタスクの 注:
shm_inc()が do_mmap() 関数コールの中で do_mmap() を呼び出したあとは、グローバル共有メモリセマフォとグローバル共有メモリスピンロックの両方が取得されます。割り当てカウントは減少されます。 shm_inc() を呼び出すため、shmat()への呼び出しに対して割り当て数の正味の変更が 1 になります。もし割り当て数の減少した後で、セグメントが破壊にマーク(SHM_DEST)されたら、 shm_destroy() が呼び出され、共有メモリセグメントの資源が解放されます。` 最後に、共有メモリのマップされる仮想メモリが、呼出し元のユーザの指定したアドレスへ返されます。 do_mmap() よりエラーコードが返されたときには、このエラーコードがシステムコールの返り値として戻されます。 sys_shmdt()sys_shmdt() の処理中はグローバル共有メモリセマフォが保持されます。現在のプロセスの ここで、 do_munmap() は共有メモリの予約を維持する関数の shm_close()をコールバックし、他の割り当てがない共有メモリセグメントの資源を解放することに注意してください。 sys_shmdt() は無条件に0を返します。 共有メモリサポート構造体struct shminfo64
struct shm_info
struct shmid_kernel
struct shmid64_ds
struct shmem_inode_info
共有メモリサポート関数newseg()newseg() 関数は、新しい共有メモリセグメントを作る必要が出たときに呼ばれます。
これは、新しいセグメントのキー、フラグ、サイズの三つのパラメータを受け取ります。作成する共有メモリセグメントのサイズが SHMMIN と SHMMAX の間にあり、共有メモリセグメントの総数が SHMALL を越えないといった有効性を確認した後に、新しい共有メモリセグメント記述子を割り当てます。
shmem_file_setup() 関数は、tmpfs 型のアンリンクされたファイルを作成するために、その後に呼び出されます。返されるファイルポインタは、共有メモリセグメント記述子の対応する shm_get_stat()shm_get_stat() は、全ての共有メモリ構造体を順に辿っていき、共有メモリに使われているメモリの全ページ数と、スワップアウトされている全メモリページ数を計算します。各共有メモリセグメントにはファイル構造体とiノード構造体があります。求めるデータをiノード経由で取得するため、アクセスされる各iノード構造体のスピンロックのロックとロック解除が、それぞれに行われます。 shmem_lock()shmem_lock() はパラメータに共有メモリ記述子へのポインタとロックかアンロックを示すフラグを受け取ります。共有メモリセグメントのロック状態は対応するiノードへ格納されます。 この状態は、要求されたロック状態と比較されます。shmem_lock() は、一致した場合に単に返ります。 対応するiノードのセマフォを保持している間に、iノードのロック状態が設定されます。各共有メモリセグメントの各ページにおいて、以下のリストの項目が実行されます。
shm_destroy()shm_destroy() の間、共有メモリページ総数は、共有メモリセグメントの削除の責任をとって調整されます。
ipc_rmid() は、(shm_rmid() 経由で)呼び出され、共有メモリID を削除します。
shmem_lock が呼び出され、共有メモリページをアンロックし、各ページの参照カウントを効率的に 0 に減算します。 fput() が呼び出され、対応するファイルオブジェクトの利用カウンタ shm_inc()shm_inc() は PID を設定し、現在の時間を設定し、そして与えられた共有メモリセグメントの付属数を1増加させます。これらの操作は、グローバル共有メモリスピンロックを保持して行われます。 shm_close()shm_close() は、 shmem_file_setup()関数 shmem_file_setup() は、 tmpfs ファイルシステムに存在するアンリンクされたファイルを、与えられた名前とサイズに設定します。もしこのファイルに対して十分なシステムメモリリソースがあれば、新しい dエントリを tmpfs のマウントルートに作成します。そして新しいファイルディスクリプタと tmpfs 型の新しい iノードオブジェクトを割り当てます。そして、d_instantiate() を呼出し、新しい d エントリオブジェクトに新しい i ノードオブジェクトを対応づけます。dエントリオブジェクトのアドレスをファイルディスクリプタへ保存します。iノードオブジェクトの 5.4 Linux IPC プリミティブセマフォ、メッセージおよび共有メモリで使用する汎用 Linux IPCプリミティブLinux のセマフォ、メッセージおよび共有メモリ機構は一連の共通プリミティブで構成されています。これらのプリミティブを以下で説明します。 ipc_alloc()メモリ割り当てが PAGE_SIZE より大きければ、vmalloc() がメモリ割り当てに使われます。そうでなければ、 kmalloc() が GFP_KERNEL 付きで呼び出されメモリを割り当てます。 ipc_addid()新しいセマフォセット、メッセージキュー、ないしはメモリセグメントが追加されると、 ipc_addid() は、 関連するディスクリプタの配列のサイズがシステムの最大値に対して十分に大きいことを保証するため、最初に grow_ary() を呼び出します。そしてディスクリプタの配列の最初の未使用要素を検索します。もし未使用の要素が見つかったら、使用中のディスクリプタのカウンタを1増加します。 新しい資源のディスクリプタのために kern_ipc_perm 構造体を初期化し、新しいディスクリプタの配列のインデックスを返します。 ipc_addid() が成功したら、与えられたIPC のタイプのグローバルスピンロックを取得して戻ります。 ipc_rmid()ipc_rmid() は、IPCディスクリプタをIPCタイプのグローバルディスクリプタ配列から削除し、 使用されている ID のカウントを更新します。そして必要なら対応するディスクリプタ配列の最大 ID を調整します。 与えられた IPC ID に対応する IPC ディスクリプタへのポインタを返します。 ipc_buildid()ipc_buildid() は与えられたIPCタイプの各ディスクリプタに対応するユニークなIDを作成します。この ID は新しいIPC要素が追加されるときに作成されます(例: 新しい共有メモリセグメントや新しいセマフォセット)。 IPC ID は対応するディスクリプタ配列のインデックスへ容易に変換できます。各IPCタイプは、ディスクリプタが追加される毎に増加させるシーケンス番号を管理しています。 ID はシーケンス番号をSEQ_MULTIPLIER 倍し、この積にディスクリプタ配列のインデックスを加算することで生成します。シーケンス番号が存在することにより、古い IPC ID の利用を検出することができます。 ipc_checkid()ipc_checkid() は、与えられた IPC IDを SEQ_MULTIPLIER で除算し、その商を対応するディスクリプタに保存されているシーケンス値と比較します。もし値が等しいなら、IPC ID は有効であると考えられ、1を返します。そうでなければ、0 を返します。 grow_ary()grow_ary() は、与えられた IPC タイプの 最大の(設定可能な) ID 数を動的に変更できる機能を取り扱います。これは現在の最大限度が恒久的なシステムの限度(IPCMNI)を越えないように強制し、必要なら引き下げます。これはまた既存のディスクリプタ配列が十分に大きくなるよう保証します。 もし既存の配列サイズが十分に大きかったら、現在の最大限度を返します。そうでなければ、新しい大きな配列を割り当てます。そして古い配列を新しい配列へコピーし、古い配列を解放します。与えられた IPC タイプのディスクリプタ配列を更新するときは、対応するグローバルスピンロックを保持します。 ipc_findkey()ipc_findkey() は、特定の ipc_ids オブジェクトのディスクリプタの配列を検索して特定のキーを探します。見つかったら、対応するディスクリプタのインデックスを返します。もしキーが見つからなかったら、-1 を返します。 ipcperms()ipcperms() は、IPC 資源にアクセスするユーザ、グループと他のパーミッションをチェックします。もしパーミッションが有効なら0を返し、そうでなければ-1を返します。 ipc_lock()ipc_lock() は、 IPC IDをパラメータの一つとして受け取ります。与えられた IPC タイプのグローバルスピンロックをロックし、特定のIPC IDに対応するディスクリプタへのポインタを返します。 ipc_unlock()ipc_unlock() は、示された IPC タイプのグローバルスピンロックを解除します。 ipc_lockall()ipc_lockall() は、与えられたIPC機構(つまり共有メモリとセマフォとメッセージング)用のグローバルスピンロックをロックします。 ipc_unlockall()ipc_unlockall() は、与えられたIPC機構(つまり共有メモリとセマフォとメッセージング)用のグローバルスピンロックを解除します。 ipc_get()ipc_get() は、特定の IPC タイプ(つまり共有メモリとセマフォとメッセージング)へのポインタとディスクリプタIDを取り、対応する IPC ディスクリプタへのポインタを返します。ここで、各 IPC タイプのディスクリプタが違うデータタイプであっても、それぞれの場合で、共通の kern_ipc_perm 構造体タイプが最初のエンティティとして埋め込まれます。 ipc_get() 関数は、この共有のデータタイプを返します。データ型を正しいディスクリプタのデータ型へキャストするラッパー関数(たとえば shm_get())を通して、ipc_get() が呼び出されることを期待するモデルになっています。 ipc_parse_version()ipc_parse_version() は、 もしあればIPC_64 フラグをコマンドから削除します。そして、 IPC_64か IPC_OLDを返します。 セマフォ、メッセージおよび共有メモリで使われる汎用 IPC 構造体セマフォ、メッセージおよび共有メモリ機構は全て以下の共有の構造体を使うようになっています。 struct kern_ipc_perm各 IPC ディスクリプタは、最初の要素にこの型のデータオブジェクトを持っています。これは、全ての汎用 IPC 関数からこのデータ型のポインタを用いて、全てのディスクリプタへアクセスできるようにするためです。
struct ipc_idsipc_ids 構造体は、セマフォとメッセージキューと共有メモリの共通データを表しています。このデータ構造体には三つのグローバルインスタンスがあります。それぞれセマフォ、メッセージ、共有メモリ用に、
struct ipc_id構造体 ipc_id の配列は、 ipc_ids 構造体の各インスタンスに存在します。この配列は動的に割り当てられ、必要に応じて grow_ary()によってより大きな配列に置き換えられることになります。 kern_ipc_perm データタイプが IPC 汎用関数によって共通ディスクリプタデータタイプとして使われるため、この配列は時々ディスクリプタ配列として参照されます。
次のページ 前のページ 目次へ |
[ |