プロセスとスレッド間のデータ交換。 IPC UNIX SystemVのメカニズムを使用したアプリケーション間の通信

16.04.2019 ソーシャルネットワーク

UnixおよびLinuxオペレーティングシステムでは、複数のプロセスで使用できるメモリセグメントを作成できます。 このツールを使用すると、異なるプロセスの仮想アドレス空間のセクションが同じ実メモリアドレスにマップされます。

共有メモリセグメントを操作する場合、プロセスの1つは、共有(共通)メモリセグメントを作成し、必要なセグメントサイズを指定して、その識別子を取得する必要があります。 この識別子は、共有メモリで制御操作を実行するために作成者プロセスによって使用されます。

共有メモリセグメントの識別子を取得したら(セグメントを作成するか、別のプロセスからその識別子を取得するかに関わらず)、プロセスはセグメントを「アタッチ」する必要があります。 追加操作は、プロセスの仮想アドレス空間内の共有セグメントのアドレスを返します。 同じセグメントの仮想アドレスは、プロセスごとに異なる可能性があります。 さらに、プロセスは、コンベンショナルメモリを使用する場合と同じマシン命令を使用して、共有メモリセグメントでデータの読み取りと書き込みを行います。

共有メモリセグメントでの作業を終了したプロセスは、それを「デタッチ」する必要があり、すべてのプロセスがセグメントの使用を終了したら、それを破棄する必要があります。

プロセスが共有メモリ領域で同時に実行されている場合、その領域へのアクセスを同期する必要がある場合があります。 この同期を確実にするのはプログラマーの責任です。これは、同時アクセスの競合を排除するアルゴリズムまたはセマフォなどのシステムツールを使用して実行できます。

Unix / Linuxシステムコール

OS Unix / Linux共有メモリセグメントメカニズムは、shmget、shmctl、shmat、shmdtの4つのシステムコールによって提供されます。

システムコール shmctlを使用すると、セグメントに対して制御操作を実行できます。つまり、その状態に関する情報を取得し、そのセグメントへのアクセス権を変更し、セグメントを破棄します。

shmatおよびshmdtシステムコールは、それぞれセグメントのアタッチおよびデタッチを実行します。

Unix / Linuxの共有メモリセグメント(セマフォなど)には外部名がありません。 セグメントIDを取得するとき、プロセスは数字キーを使用します。 無関係なプロセスの開発者は同意することができます 一般的な意味使用するキーですが、同じキー値が他の誰かによって使用されないという保証はありません。 保証された一意のセグメントは、IPC_PRIVATEキーを使用して作成できますが、そのようなキーを外部にすることはできません。 したがって、セグメントは、原則として、継承されたリソースや子プログラムの呼び出しパラメーターなどを介して、相互に識別子を渡すことができる関連プロセスによって使用されます。

注意!
プログラムをデバッグする過程で、プログラムが作成した共有メモリ領域を破壊する前に、プログラムがクラッシュしたり、中断されたりする場合があります。 このような領域はシステムから自動的に削除されることはなく、何日にもわたって蓄積される可能性があります。 このような「忘れられた」共有エリアの蓄積は、共有エリアの数に対するシステム制限が使い果たされるという事実につながる可能性があり、次の呼び出し

Linuxのプログラミング。 パート2。

LinuxAPI-インタープロセスの概要

交流

アンドレイ・ボロフスキー

シンプルでシンプルなUnixシステムでの存在 効果的な手段相互作用

プロセス間は、Unixプログラミングにも同様に重要な影響を及ぼしました

システムオブジェクトをファイルとして表すよりも影響力があります。 おかげで

プロセス間通信(IPC)開発者

(およびユーザー)は、複雑な問題の解決策をいくつかに分割できます 簡単な操作、それぞれが個別の小さなプログラムによって信頼されています。

いくつかの単純なプログラムによる1つのタスクの順次処理は、パイプラインの作成と非常に似ています(英語のパイプラインの多くの意味の中に「パイプライン」もありますが、この記事では、国内の文献で受け入れられている「チャネル」という用語を使用しますパイプという言葉を翻訳します。

パイプラインアプローチの代替手段は、大規模なモノリシックなオールインワンパッケージです。 用途を設定する シンプルなユーティリティ 1つの複雑な問題を解決するには、ユーザーの側でもう少しスキルが必要ですが、その見返りとして、モノリシックな「モンスター」では達成できない柔軟性が提供されます。 オープンIPCプロトコルを使用するユーティリティのセットは、簡単に拡張および変更できます。 複雑なタスクを比較的小さなサブタスクに分割すると、プログラマーが犯すエラーの数も減ります(サイドバーを参照)。 これらすべてに加えて、IPCにはもう1つの重要な利点があります。 IPCを使用するプログラムは「話す」ことができます

ユーザーとほぼ同じくらい効果的に相互に連携するため、複雑なタスクを自動化できます。

UnixおよびLinuxスクリプト言語の能力は、主にIPCの能力に基づいています。

この記事では、さまざまなタイプのチャネルを使用するIPCに限定します。 記事の読者は経験豊富であると想定されています Linuxユーザー、そして、いずれにせよ、彼らはチャネルが使用するいくつかのプログラムからどのように作成されるかを知っています コマンドライン。 プログラマーの観点からは、記号「|」で構成されたチャネル内のプログラムの操作は非常に単純に見えます。 あるプログラムの標準出力からのデータは、別のプログラムの標準入力にリダイレクトされます。別のプログラムの標準出力もリダイレクトされる場合があります。 しかし、プログラム自体の中でチャネルを使用する必要がある場合はどうでしょうか。

ブルックスの法則TheMythicalMan-Monthの著者であるFrederickBrooksは、プロジェクトのエラーの数はプロジェクト参加者の数の2乗に比例し、その量は 役に立つ仕事線形関係によって参加者の数に関連しています。

ブルックスの法則が満たされた場合、プロジェクトの開発の特定の段階で、新しい開発者を引き付けようとすると、雪崩のようなエラーの数が増加し、それらを修正する必要があると、すべてのリソースが吸収されます。プロジェクトの。 言い換えれば、ソフトウェアプロジェクトに関するブルックスの法則によれば、複雑さには一定のしきい値があり、それを超えると効率が急速に低下します。 はどうかと言うと オープンモデルソフトウェア開発は、ブルックスの法則の観点からは、原則として不可能であるはずです。 F.ブルックスが何について間違っていたかを理解するために、彼の推論の最初の前提を考慮する必要があります。 ブルックスの法則は、次の2つの仮定に基づいています。(a)プロジェクト要素間のインターフェイスでエラーが発生する可能性が高い さまざまな開発者(したがって、そのような「継ぎ目」が多いほど、エラーも多くなります)。

(b)開発者相互作用モデルは完全グラフであり(つまり、各開発者は他のすべてのプロジェクト参加者と相互作用します)、そのエッジの数は頂点の数の2乗に比例します。 一般に、どちらのステートメントも誤りではありません。 特に、複雑な問題の解決策がいくつかの小さなユーティリティに分散されている場合、すべてのプロジェクト参加者が互いに直接対話する必要はありません。 各開発チームは、プログラム間で通信するために固定プロトコルのみに従う必要があります。そのため、この場合、エラーの数は2次ではなく、線形になります。

名前のないパイププログラム内パイプの最も一般的な使用法は、プログラムが別のプログラムを開始し、標準出力に書き込むデータを読み取る場合です。 このトリックを使用すると、開発者は、その操作の内部の詳細に干渉することなく、自分のプログラムで別のプログラムの機能を使用できます。 この問題を解決するために、popen(3)とpclose(3)を使用します。 正式には、これらの関数はfopen(3)およびfclose(3)に似ています。 popen()関数は、外部プログラムを開始し、実行中のプロセスの標準入力ストリームまたは標準出力ストリームのいずれかに関連付けられたFILE構造体へのポインターを呼び出し元のアプリケーションに返します。 popen()関数の最初のパラメーターは、外部プログラムを開始するコマンドを含む文字列です。 2番目のパラメーターは、返される標準ストリーム(出力または入力)を指定します。 「w」引数は、実行中のプログラムの入力ストリームに対応します。この場合、popen()を呼び出したアプリケーションはデータをストリームに書き込みます。 「r」引数は出力ストリームに対応します。 pclose()関数は次のように終了します 外部アプリケーションチャンネルを閉じます。 popen()/ pclose()関数の操作方法を示すために、次のように記述します。 小さなプログラム makelog( 全文プログラムは、ディスク上のmakelog.cファイルにあります。makelogプログラムは、パラメーターとして渡されたシェルコマンドを実行し、このコマンドの出力を標準端末とlog.txtファイルの両方に書き込みます(同様の関数には 標準コマンドティー)。 たとえば、プログラムをコンパイルする場合:



ターミナル画面でgccmakelog.c -omakelogの後にmakelog "ls -al"が続くと、シェルコマンドls -alの出力が出力され、同じ出力を含むlog.txtファイルが作業ディレクトリに作成されます。 makelogプログラムの。 makelogプログラムがコマンドラインを単一のコマンドラインパラメータとして受け取るためには、シェルコマンドを引用符で囲む必要があります。

読者はおそらく、makelogプログラムのハイライトはpopen()関数の使用であるとすでに推測しています。 プログラムのソースコードの断片を考えてみましょう。

f = popen(argv、 "r");

この操作は、通常のファイルを開いて読み取るのと非常によく似ています。

変数fのタイプはFILE *ですが、popen()のargvパラメーターはファイル名ではなく、プログラムを実行するコマンドまたは「ls-al」などのシェルコマンドです。 popen()呼び出しが成功した場合、通常のfread(3)関数を使用して実行中のコマンドの出力を読み取ることができます。

fread(buf、1、BUF_SIZE、f)popen()関数の機能は、渡されたコマンドが無効であっても、この関数がNULLを返さないことです。

この状況でエラーを検出する最も簡単な方法は、出力ストリームからデータを読み取ろうとすることです。 出力ストリームにデータがない場合(fread()は0を返します)、エラーが発生しています。 fread()で読み取ったデータを端末に出力するには、標準の出力ストリームへのハンドルを指定してwrite()関数を使用します。

write(1、buf、len);

並行して、同じデータがディスク上のファイルに書き込まれます。 データを読んだ後 開水路閉じる必要があります:

pclose()は、popen()で開始されたアプリケーションが終了した後にのみ、呼び出し元のスレッドに制御を返すことに注意してください。

結論として、popen()関数のもう1つの機能に注目します。 渡されたコマンドを実行するために、popen()は最初にシェルの独自のインスタンスを起動します。これは、一方では優れていますが、他方ではあまり良くありません。 これは、popen()を呼び出すと、内部シェル操作(ファイル名パターンの処理など)が自動的に実行され、PATHやHOMEなどの環境変数が使用されるためです。 popen()アプローチの欠点は、シェルプロセスを開始するための追加のオーバーヘッドです。これは、次の場合は不要です。 外部プログラムシェル自体は必要ありません。

popen()関数は、チャネルを暗黙的に使用して外部アプリケーションと通信します。 私たちのプログラムでは、チャンネルを直接使用できます。 最も一般的なタイプのパイプは、pipe(2)関数によって作成された無名パイプです。 プログラミングインターフェイスレベルでは、このようなパイプは2つのファイル記述子で表されます。1つはデータの読み取りに使用され、もう1つは書き込みに使用されます。 チャネルはランダムアクセスをサポートしていません。つまり、データは書き込まれたのと同じ順序でのみ読み取ることができます。

名前のないパイプは、主にfork(2)関数と組み合わせて使用​​され、親プロセスと子プロセスの間でデータを交換するために使用されます。 このようなデータ交換を整理するには、まず、pipe()関数を使用してチャネルを作成します。 pipe()関数に渡される唯一のパラメーターは、2つの要素で構成されるint型の配列です。 配列の最初の要素では、関数はチャネルからデータを読み取るために使用されるファイル記述子(チャネル出力)を返し、2番目の要素では書き込み用の記述子(入力)を返します。 次に、fork()関数を使用して、プロセスが「フォーク」されます。 子プロセスは、pipe()で開かれた両方のハンドルを親プロセスから継承しますが、親プロセスと同様に、一方のハンドルのみを使用する必要があります。

親プロセスと子プロセスの間のデータ転送の方向は、親プロセスが使用するハンドルと子プロセスが使用するハンドルによって決まります。 上記をデモンストレーションしましょう 簡単な例 pipe()およびfork()関数を使用するpipes.cプログラム。

#include #include #include int main(int argc、char * argv)int pipedes;

char * str = "パイプを介して渡された文字列\ n";

write(pipedes、(void *)str、strlen(str)+ 1);

while((len = read(pipedes、buf、1024))!= 0)write(2、buf、len);

両方のパイプ記述子は、pipedes変数に格納されます。 fork()が呼び出された後、プロセスforkと親プロセス(fork()が子プロセスのPIDに等しいゼロ以外の値を返したプロセス)は読み取りハンドルを閉じ、書き込みを使用してパイプにデータを書き込みますハンドル(パイプ)。 子プロセス(fork()が0を返した)は、書き込みハンドルを閉じてから、読み取りハンドル(pipedes)を使用してパイプからデータを読み取ります。 記述子の目的は、略語I / Oと比較することで簡単に覚えられます(最初の記述子は読み取り(入力)用で、2番目は書き込み(出力)用です)。 POSIX標準では、両方のパイプ記述子を受け取った各プロセスは、別の記述子に進む前に、不要なパイプ記述子を閉じる必要がありますが、 Linuxシステムこの要件は無視できます。厳格なルールに従うことをお勧めします。

この例では、システムカーネルがすべてのハードワークを実行するため、データ転送の同期について心配する必要はありません。 しかし、人生にはそれほど些細なこともありません。 たとえば、複数のfork()呼び出しを使用して複数の子プロセスを作成することを妨げるものは何もありません。 各プロセスが意図したとおりにpipdesハンドルの1つのみを使用する場合、これらのプロセスはすべて同じパイプを使用できます。

この状況では、データ転送の同期を明示的に実行する必要があります。

パイプがデータを転送する方法パイプは、(システムカーネルによって作成された)メモリの特別な領域を使用して、パイプバッファと呼ばれるデータを転送します。

チャネルバッファの重要な機能の1つは、前のエントリがバッファを完全に満たしていない場合でも、 再突入バッファへのデータは、以前に書き込まれたデータが読み取られた後にのみ可能になります。 これは、同じパイプにデータを書き込む異なるプロセスが、サイズがバッファのサイズを超えないブロックでデータを送信する場合、書き込まれるブロックからのデータが書き込まれることを意味します さまざまなプロセス、互いに混ざりません。 チャネルのこの機能を使用すると、データ送信の同期が大幅に簡素化されます。 関数fpathconf(pipedes、_PC_PIPE_BUF)を呼び出すと、バッファーのサイズを確認できます。ここで、pipedesはパイプ記述子です。 IA32プラットフォームでは、1つのチャネルバッファのサイズは4キロバイトです。 カーネルバージョン2.6.11以降、各Linuxチャネルは最大16個のバッファーを使用できるため、チャネルのパフォーマンスが大幅に向上します。

名前のないパイプに慣れてきたので、「追加コスト」なしで(つまり、シェルプロセスを開始せずに)、popen()関数のアナログを独立して実装できます。 実行する小さなプログラムを書いてみましょう netstatユーティリティ、このユーティリティの出力を読み取り、画面に出力します。 この目的でpopen()関数を使用する場合は、popen( "netstat"、 "r");を呼び出してnetstat出力ストリームにアクセスします。

この方法は単純ですが、効率的ではありません。 別のプログラム(printns.cファイル)を作成します。 このプログラムの構造は前の例と同じですが、親プロセスがパイプを使用してデータを明示的に読み取るようになっただけです。

最も興味深いことは、一連の関数が実行される子プロセスで発生します。

dup2(パイプ、1);

execve( "/ bin / netstat"、NULL、NULL);

dup2(2)では、子の標準出力(stdoutハンドル1)を、pipdesの書き込み可能なハンドルを使用してパイプにリダイレクトします。 次に、execve(2)を使用して子プロセスイメージをnetstatプロセスに置き換えます(PATH環境変数を持つシェルがないため、netstat実行可能ファイルへのフルパスを指定する必要があることに注意してください)。

名前付きパイプ上記の例では、名前なしパイプのみを使用して関連するプロセス間でデータを転送していますが、それらを使用して完全に異なるプロセス間でデータを転送することもできます。 これを行うには、たとえばで説明されているように、無関係なプロセス間でのチャネル記述子の転送を整理する必要があります。 ただし、記述子を渡す サードパーティのプロセスはトリック(または「ハック」)キャラクターであり、私たちはそれにこだわるつもりはありません。 無関係なプロセス間でデータを転送するには、名前付きパイプメカニズムを使用します。これにより、各プロセスが独自の「正当な」パイプ記述子を取得できるようになります。 これらのチャネルでのデータ送信(実際、単方向の名前のないチャネルでのデータ送信)は、FIFOの原則(最初に書き込まれる-最初に読み取られる)に従うため、英文学では、FIFOパイプまたは単にFIFOという名前が見つかることがあります。 名前付きパイプは、名前付きパイプ(奇妙なことではありませんか?)、つまり、システム上のすべてのプロセスに表示される可能性のあるパイプ識別子があるという点で、名前なしパイプとは異なります。 名前付きパイプを識別するために、特殊なタイプのパイプのファイルが作成されます。 これは、データストレージを目的としていないUnix仮想ファイルファミリの別のメンバーです(パイプファイルサイズは常にゼロです)。 名前付きパイプファイルは、通常のファイルと同様にVFSメンバーです。 Linuxファイル、および同じアクセス制御ルールがそれらに適用されます。 mkfifo(3)関数を使用して、名前付きパイプファイルを作成できます。 この関数の最初のパラメーターはチャネルファイルの名前を含む文字列であり、2番目のパラメーターはファイルアクセス権のマスクです。 mkfifo()関数は、適切なタイプのパイプとファイルを作成します。 もしも 指定されたファイルチャネルはすでに存在し、mkfifo()は-1を返します(errnoはEEXISTに設定されます)。 パイプファイルが作成されると、通信に関係するプロセスは、書き込みまたは読み取りのためにファイルを開く必要があります。 チャネルファイルが閉じられた後も、ファイル(および対応するチャネル)は引き続き存在することに注意してください。 チャネル自体を閉じるには、たとえば、連続するunlink(2)呼び出しを使用して、そのファイルを削除する必要があります。

例として、単純なクライアントサーバーシステムを使用して、名前付きパイプがどのように機能するかを見てみましょう。 サーバープログラムはチャネルを作成し、ユーザーがキーボードから入力したテキストをチャネルに渡します。 クライアントプログラムはテキストを読み取り、端末に出力します。 この例のプログラムは、マルチユーザーOSのユーザー間のインスタントメッセージングシステムの簡略化されたバージョンと見なすことができます。 サーバープログラムのソースコードは、typeserver.cファイルに保存されています。 mkfifo()関数を呼び出すと、プログラムの作業ディレクトリにチャネル識別子ファイルが作成されます。

mkfifo(FIFO_NAME、0600);

ここで、FIFO_NAMEは、チャネルファイル(この場合はfifofile)の名前を指定するマクロです。

アクセスマスクとして、8進数の値0600を使用します。これにより、同様のユーザー資格情報を持つプロセスが読み取りと書き込みを行うことができます(マスク0666を使用できますが、念のため、8進数であっても、獣の数については言及しません。 、念のため私たちのプログラムで)。 簡潔にするために、mkfifo()によって返される値にエラーがないかどうかはチェックしません。 指定されたパラメータを使用してmkfifo()を呼び出した結果、プログラムの作業ディレクトリに特別なファイルfifofileが表示されます。 KDEファイルマネージャは、半分開いた蛇口を表すきれいなアイコンでチャンネルのファイルを表示します。 次に、サーバープログラムで、作成したファイルを開いて書き込みます。

f = fopen(FIFO_NAME、 "w");

ユーザー入力はgetchar()を使用して読み取られ、データはfputc()を使用してパイプに渡されます。 ユーザーが文字「q」を入力すると、サーバーは終了します。 クライアントプログラムのソースコードは、typeclient.cファイルにあります。 クライアントは、通常のファイルとして読み取るためにfifofileを開きます。

f = fopen(FIFO_NAME、 "r");

パイプを介して送信された文字は、fgetc()関数を使用して読み取られ、putchar()を使用して端末画面に表示されます。 サーバーのユーザーが入力をタップするたびに、サーバーによって呼び出されるfflush()関数(を参照)

file typesserver.c)は、チャネルのバッファの強制フラッシュを実行し、クライアントに送信されたすべての文字を読み取らせます。 文字「q」を受信すると、クライアントは終了します。

プログラムtypeserver.cとtypeclient.cを同じディレクトリにコンパイルします。

最初にサーバーを実行し、次に別のターミナルウィンドウでクライアントを実行します。 サーバーウィンドウにテキストを入力します。 各キーストロークの後、クライアントはサーバーに印刷された文字列を表示する必要があります。

mknod(2)関数を使用してFIFOファイルを作成することもできます。この関数は、さまざまなタイプのファイル(FIFO、ソケット、デバイスファイル、および通常のデータファイル)を作成するように設計されています。 この場合、mkfifo(fname、0600);の代わりに

mknod(fname、S_IFIFO、0);と書くことができます。

の一つ 強み Unix / Linux IPCは、相互に何も知らないだけでなく、異なるI / Oメカニズムを使用するプログラム間の相互作用を整理する機能です。 typeclientプログラムとlsコマンドを比較してみましょう。 それらの間に共通点はないように思われます。typeclientは名前付きパイプを使用してデータを受信し、lsはディレクトリの内容を標準の出力ストリームに出力します。 ただし、いくつかのbashコマンドを使用して、lsからtypeclientにデータを渡すように調整できます。 typeclientプログラムディレクトリで、次のコマンドを発行します。

mknod fifofile pこのコマンドは、typeserverと同じようにfifofileパイプファイルを作成します。 typeclientプログラムを実行してから、別のターミナルウィンドウでls -al> / path / fifofileのようなコマンドを発行します。ここで、/ path / fifofileはFIFOファイルへのパスです。 その結果、typeclientプログラムは、対応するディレクトリの内容を出力します。 主なことは、その作業を完了する「q」文字がデータストリームに出現しないことです。

チャンネルはシンプルで 便利なツールただし、データ送信はすべての状況に適しているわけではありません。 たとえば、チャネルを使用してプロセス間で非同期メッセージを交換することは非常に困難です。 次の記事では、他のUnix / Linux IPC機能、メッセージキュー、およびセマフォについて説明します。

1. D. P Bovet、M。Cesati、Understanding the Linux Kernel、第3版、O "Reilly、2。WRStevens、SA Rago、UNIX®環境での高度なプログラミング:第2版、Addison Wesley Professional、3。StevensW 。、UNIX:プロセスの相互作用-サンクトペテルブルク:ピーター、



同様の作品:

「ロシア連邦教育科学省の注目! このファイルには、国民経済の支部向けのすべてのプログラムが含まれています。 プログラム-専門分野の最小候補者試験08.00.05-経済学の経済学と国民経済の管理(経済学、企業、産業、農産業複合体と農業の複合体の組織と管理)最小プログラムは24ページで構成されています。 2007 2はじめにこのプログラムの基礎は、次の分野の重要な規定でした:... "

「2014年7月1日付けのOGBPOUコストロマ工科大学第21p号の所長の命令により承認されました。地域の州予算職業教育機関コストロマ工科大学1の学生の追放、回復、転校の手順。 一般規定 1.1。 地域国家予算職業教育機関コストロマ工科大学(以下、-... ")の学生の退学、回復および転校のためのこの手順

「指定された契約は、アートに基づいて公開されています。 ウクライナ民法第633条に基づき、消費者に関連するサービスを提供する機会があれば、彼に連絡するすべての人に本契約の対象となるサービスを提供することを会社に義務付けています。 サービス提供に関する合意(2013年1月22日に修正)Sevastopol _201 PE Sevtelekomservis、Evgeny Alexandrovich Prividentsev局長が代表し、憲章に基づいて行動し、以下、...と呼びます。

「クルガン幼稚園第79ブルック市の市立幼稚園教育機関の2011〜2012年度の公開報告書1.一般的特徴教育機関の氏名:市立保育園 教育機関クルガン幼稚園第79ブルック市の。 正式略称:MBDOU幼稚園第79号。a)教育機関の設立日:1964年6月。 教育機関の創設者:クルガン市の自治体は... "

「教育のための連邦機関アナパのソチ州立観光リゾートビジネスブランチ私はAFSGUTとKDI.Iのディレクターを承認します。 Ashkinadze社会文化サービスと観光規律作業プログラム(OZFO)の革新部門に割り当てられた:社会文化サービスと観光カリキュラム:100103専門分野:社会文化サービスと観光作業カリキュラムによる120時間:州による時間教育水準(RUPから):作業プログラムに応じた120時間:約...に応じた時間」

Lutiy 2013 MARYKAYでサクセスストーリーを作成する方法はあなた次第です。 メアリーケイはあなたに美しさへの道を教えてくれます! 新しい新人ビジネスアトラクションと開発プログラムに会いましょう! 収益性の高い提案 2012〜2013年度セミナー第2四半期のスターコンサルタントのみ! 2013年2月1日から3月31日まで、セミナー年度の第2四半期(2012年11月から2013年1月)のすべてのスターコンサルタントに対して、当社は独占的な買収の機会を提供します...»

「ロシア連邦教育科学省FGBOUVPOクラスノヤルスク州立教育大学はV.I.にちなんで名付けられました。 V.P. アスタフィエバ生物学・地理・化学学部化学科超分子化学の基礎M.2学問分野の教育的および方法論的複雑性研究の方向性:050100.68 教師教育修士課程化学教育クラスノヤルスク2011 ワーキングプログラム Assocによって編集されました。 化学科、Ph.D。 Beresnev V.A.、Ph.D。 クズネツォワA.S. 作業プログラムはで議論されました...»

«ロシア連邦農業省高等専門教育の連邦州予算教育機関クバン州アグラリアン大学ワーキングプログラム規律GSE。 B.04専門分野の工学心理学190601.65自動車および自動車産業学部:機械化主要部門:教育学および心理学タイプ 学術研究フルタイム教育合計時間コース、学期講義3年目、165学期...»

「私はIMCESSB RASのディレクター、物理数理科学の博士を承認します。 _ V.A. Krutikov _ 2012年にロシア科学アカデミーのシベリア支部の気候および生態系を監視する研究所の科学ステーションTaigaを支援するために受け取った研究の結果と資金の使用に関するレポート。ニベガの村、Verkhneketsky Priketyeの領土にあるTomsk地域の地区-東部で最も森林に覆われた地域の1つ... "

「注釈。 作業プログラムは、以下に基づいて開発されました。連邦基本カリキュラム(2004年3月9日付けのロシア国防省の命令)第1312号。 2004年3月5日のロシア連邦教育省の命令第1089号(2009年10月19日に修正)初等一般、基礎一般および中等(完全)一般教育のための州教育基準の連邦コンポーネントの承認について; 2013年8月19日付けのベラルーシ共和国国防省の命令第1384号ベラルーシ共和国の教育機関に推奨されるBUPと模範的なカリキュラムについて...」

"。 市立予算教育機関中等学校第1号が承認されました。 教師評議会に受け入れられました。 人道支援サイクルディレクター(2013年8月29日付けの議事録第1号)の教師のMS(2013年8月30日付けの注文番号34)(2013年8月30日付けの議事録第11号)によって検討されました。_G.P。Khlyustova 英語 10年生の教師:Strokova Nina Stepanovna Kimovsk 1注釈作業プログラムは、連邦政府の構成要素に基づいています。 州の基準一般..."

「2013年12月19日のペンザ地域決定No.972-pPの政府、ペンザ政府の法令によって承認された、2014年から2020年までのペンザ地域の農業産業複合施設の国家プログラム開発の修正に関するペンザ2013年9月18日の地域No.691-pP 2005年12月22日付けのペンザ地域の法律に基づく現在の法律に従って規範的な法的行為を行うために、No。906-ZPOペンザ地域の政府について(と ... "

「研究されている主題の本質を理解するための美的(感情的価値)および解釈学的(比喩的-概念的、意味論的)態度の読者の心の形成; 2)発達課題:芸術的な言葉の感覚、文学的嗜好の発達; コミュニケーションスキルの開発; スピーチの発達; 3)教育的課題:文学作品の創造的知覚の文化の形成。 精神性の源としての本に対する美的態度の教育、反映...」

「付録2連邦政府高等専門教育予算教育機関カリーニングラード州立工科大学(FGBOU VPO KSTU)は、2012年の入学試験プログラムから科学専門分野の大学院研究までNR AVIvanovの副学長によって承認されました。 -理論的確率と数学的統計カリーニングラード201216入学試験のプログラムは、技術科学博士、V.A教授によって開発されました。...」

「122ロシア連邦政府サンクトペテルブルク州立大学登録番号CM / 060201/1専門付録060201サンクトペテルブルク州立大学の教育水準に対する歯科サンクトペテルブルク州の教育水準に対する高等専門教育のレベル... "

"1。 適用分野1.1。高度なトレーニングプログラム自治体におけるイノベーション活動の管理は、自治体の従業員、クラスノダール地方の自治体機関の従業員を対象としています。 1.2。学生が習得した専門知識の適用範囲は、地方自治体の機能の実施と提供における従業員の活動です。 地方自治体のサービス。 2.プログラムによるトレーニングの特徴2.1。プログラムを習得するための標準的な用語は72時間です....」

「国際労働局理事会第322回会期、ジュネーブ、2014年10月30日〜11月13日GB.322INS議事録およびプログラム制度問題セクション議事録第321回理事会の議事録の承認。 1.国際労働会議の議事。 2. ILO宣言が社会正義に与える影響を評価する準備をする3.第105回国際労働会議(2016年)の公正なグローバリゼーションのために。 発生する質問...」

Shraiberg Ya.L.、Goncharov M.V.、Shlykova O.V. 図書館のためのインターネットリソースとサービス:教科書。 手当。 -M。:B.i.、2000.-140p。 シュライダーYu.A. 理論への言語学的アプローチ 情報システム//科学的および技術的情報、1962年。-No。9. Schreider Yu.A. 情報学と理論的意味論におけるシソーラス// NTI。 Ser。 2.-1971.-№3。エコU.インターネットからグーテンベルクへ:テキストとハイパーテキスト。 -アクセスモード:http://www.artinfo.ru/text電子画像と視覚芸術:相互の資料...»

"地理。 の基本レベル。 10-11クラス。 (68時間)チュートリアル:A.P。 クズネツォフ、E.V。 キム地理。 の基本レベル。 10〜11個のセル。 M。:ノガン、2012年。 プログラム:地理学の基礎一般教育のプログラム。 5〜9学年。 著者A.I. アレクシーフ、O.A。 クリマノフ、V.V。 クリマノフ、V.A。 ニゾフツェフ、E.V。 キム。 (出典地理学MIOOの方法論研究室)注釈:コース地理学。 の基本レベル。 General Geography and Country Studies(O.A。Klimanova、... "

«展示会プログラム内容MetroCash&Carry RussiaのCEOによる紹介WWW.METRO-EXPO.RU内容ロシア、ウクライナ、ベラルーシのユニリーバグループ企業の社長による紹介。 5展示会の計画参加者リストスタンドM1。 レストランラカルテスタンドM2。 カフェスタンドM3。 コンビニビーンズスタンドM4。 オフィスワールドスタンドM5。 肉および鶏肉スタンドM6。 魚介類スタンドM7。 チーズと珍味はM8に立ちます。 果物と野菜スタンドM9。 デリバリースタンドM10。 品質管理..."

メモリのすべての領域にアクセスできるオペレーティングシステムは、 情報交換アプリケーションストリーム。 データを交換する必要がある場合、スレッドはOSにリクエストを送信します。 OSは、その特権を使用して、さまざまなシステム通信手段を作成します。 プロセス間データ交換ツールの多くは、同期機能も実行します。受信プロセスのデータがない場合、OSによって後者は待機状態になり、送信プロセスからデータが到着すると、受信プロセスは待機状態になります。プロセスがアクティブ化されます。

転送はいくつかの方法で行うことができます。

- 共有メモリ;

-チャネル(パイプ、コンベヤー)-あるプロセスが書き込み、別のプロセスが読み取る疑似ファイル。

-ソケット-カーネルによってサポートされるメカニズムで、環境の機能を隠し、同じコンピューターとネットワークの両方でプロセスが均一に相互作用できるようにします。

-メールボックス(Windowsのみ)、単方向、ブロードキャスト機能。

-リモートプロシージャコール、プロセス Aプロセス内のプロシージャを呼び出すことができます V、データを取り戻します。

コンベヤー(チャネル)

パイプを使用して双方向接続を確立するには、無名パイプと名前付きパイプの2つの方法があります。 チャネルはのバッファです ランダム・アクセス・メモリ、FIFOバイトキューをサポートします。 プログラマーにとって、このバッファーは、書き込みと読み取りが可能な名前のないファイルのように見え、それによってデータを交換します。 関連するプロセスのみがデータを交換できます。より正確には、このパイプラインを作成した共通の祖先を持つプロセスです。 これは、パイプラインに名前がなく、各プロセスのローカル値を持つ記述子によってアクセスされるためです。

名前のない(または匿名の)パイプを使用すると、関連するプロセスが相互に情報を渡すことができます。 通常、無名パイプは、子プロセスの標準入出力をリダイレクトして、親プロセスと通信できるようにするために使用されます。 双方向でデータを交換するには、2つの匿名チャネルを作成する必要があります。 親プロセスは書き込みハンドルを使用して最初のパイプにデータを書き込み、子プロセスは読み取りハンドルを使用してパイプからデータを読み取ります。 同様に、子プロセスは2番目のパイプにデータを書き込み、親プロセスはそこからデータを読み取ります。 名前のないパイプを使用して、ネットワークを介してデータを転送したり、無関係なプロセス間で交換したりすることはできません。

名前付きパイプライン。 このようなパイプラインの名前はOSファイルシステムディレクトリのエントリであるため、2つの任意のプロセスまたはこれらのプロセスのスレッド間でデータを交換するのに適しています。 名前付きパイプラインは 特別ファイル FIFOタイプで、ディスク上にデータ領域がありません。 名前付きパイプラインは、任意のタイプのファイルを作成するために使用されるのと同じシステムコールを使用して作成されますが、ファイルタイプとしてFIFOパラメーターが指定されている場合のみです。 システムコールは、次のディレクトリにFIFOファイルエントリを作成します。 、その後、任意のプロセスがこのファイルを開き、この名前のファイルを開いた別のプロセスにデータを転送できます。

名前付きパイプは、独立したプロセス間またはで実行されているプロセス間でデータを転送するために使用されます 別のコンピューター。 通常、名前付きパイプサーバープロセスは、既知の名前またはクライアントに渡される名前で名前付きパイプを作成します。 名前付きパイプのクライアントプロセスは、作成されたパイプの名前を知っており、サーバープロセスによって指定された制限に従って、パイプをその側で開きます。 その後、サーバーとクライアントの間に接続が作成され、それを介して双方向でデータを交換できます。 将来的には、名前付きパイプが私たちにとって最大の関心事になるでしょう。

米。 2.22チャネル実装スキーム

メッセージキュー

メッセージキューメカニズムはパイプラインメカニズムに似ていますが、プロセスとスレッドが構造化されたメッセージを交換できるという違いがあります。 メッセージキューは グローバルとはプロセスのコミュニケーション オペレーティング・システム、OS内の各キューには一意の名前があるためです。

共有メモリ

共有メモリはセグメントです 物理メモリ 2つ以上のプロセスの仮想アドレス空間にマップされます。

米。 2.23 a)メモリマップトファイル。 b)共有メモリ

メモリマップトファイルの利点の1つは、共有が簡単なことです。 「ファイルマッピング」オブジェクトに名前を付けると、それが可能になります 共有複数のプロセスによるファイル。 この場合、その内容は共有物理メモリにマップされます。

メールボックス

メールボックスは単方向接続のみを提供します。 メールボックスを作成する各プロセスは「サーバー」です メールボックス"。 「メールボックスクライアント」と呼ばれる他のプロセスは、メールボックスにメッセージを書き込むことによってサーバーにメッセージを送信します。 着信メッセージは常にメールボックスに追加され、サーバーがそれらを読み取るまで保存されます。 各プロセスは同時にメールボックスサーバーとメールボックスクライアントの両方になることができるため、プロセス間に双方向通信が作成されます。

プロセス間通信( プロセス間通信(IPC))は、プロセススレッド間でデータを交換するための一連のメソッドです。 プロセスは、同じコンピューターとネットワークで接続された異なるコンピューターの両方で起動できます。 IPCには、「シグナル」、「ソケット」、「セマフォ」、「ファイル」、「メッセージ」など、いくつかのタイプがあります。

この記事では、3種類のIPCのみを検討します。

余談:この記事は教育的であり、道を歩み始めたばかりの人々を対象としています システムプログラミング。 彼女の主な目標は 違う方法 POSIX準拠のOS上のプロセス間の相互作用。

名前付きパイプ

ソケット、チャネル、Dバス、およびその他のテクノロジーを使用してメッセージを送信できます。 隅々にあるソケットについて読むことができますが、D-busについては別の記事を書いてください。 そのため、POSIX標準に適合し、実用的な例を示す、目立たないテクノロジに焦点を当てることにしました。

名前付きパイプを介してメッセージを渡すことを検討してください。 概略的には、転送は次のようになります。

名前付きパイプを作成するには、関数を使用します。 mkfifo():
#含む int mkfifo(const char * pathname、mode_t mode);
この関数は、という名前の特別なFIFOファイルを作成します パス名、およびパラメータ モードファイルのアクセス許可を設定します。

ノート: モード現在の値と組み合わせて使用 umask次のように: (モード&〜umask)。 この操作の結果は新しい値になります umask作成しているファイルの場合。 このため、0777( S_IRWXO | S_IRWXG | S_IRWXU)現在のマスクのビットを上書きしないようにします。
ファイルが作成されると、どのプロセスでも、通常のファイルを開くのと同じ方法で、読み取りまたは書き込みのためにファイルを開くことができます。 ただし、ファイルを正しく使用するには、2つのプロセス/スレッドで同時に開く必要があります。1つはデータの受信(ファイルの読み取り)用、もう1つは送信(ファイルへの書き込み)用です。

FIFOファイルの作成に成功した場合、 mkfifo() 0(ゼロ)を返します。 エラーが発生した場合、関数は-1を返し、エラーコードを変数に設定します errno.

チャネルの作成中に発生する可能性のある一般的なエラー:

  • EACCES-パス内のディレクトリの1つで実行(実行)する権限がありません パス名
  • 存在- ファイル パス名ファイルがシンボリックリンクであっても、すでに存在します
  • ENOENT-で言及されているディレクトリはありません パス名、または壊れたリンクです
  • ENOSPC-新しいファイルを作成するスペースがありません
  • ENOTDIR-で言及されているディレクトリの1つ パス名、実際にはそうではありません
  • EROFS-でFIFOファイルを作成しようとします ファイルシステム読み取り専用
作成したファイルの読み取りと書き込みは、関数を使用して実行されます 読む()書きます().

mkfifo.c
#含む #含む #含む #含む #define NAMEDPIPE_NAME "/ tmp / my_named_pipe" #define BUFSIZE 50 int main(int argc、char ** argv)(int fd、len; char buf; if(mkfifo(NAMEDPIPE_NAME、0777))(perror( "mkfifo"); return 1;)printf( "%s is created \ n"、NAMEDPIPE_NAME); if((fd = open(NAMEDPIPE_NAME、O_RDONLY))<= 0) { perror("open"); return 1; } printf("%s is opened\n", NAMEDPIPE_NAME); do { memset(buf, "\0", BUFSIZE); if ((len = read(fd, buf, BUFSIZE-1)) <= 0) { perror("read"); close(fd); remove(NAMEDPIPE_NAME); return 0; } printf("Incomming message (%d): %s\n", len, buf); } while (1); } [скачать ]

ファイルを読み取り専用で開きます( O_RDONLY)。 そして使用することができます O_NONBLOCK反対側が書き込みのためにファイルを開くのを待たないように、FIFOファイル用に特別に設計された修飾子。 しかし、上記のコードでは、この方法は不便です。

プログラムをコンパイルしてから実行します。
$ gcc -o mkfifo mkfifo.c $ ./mkfifo
次のターミナルウィンドウで、次を実行します。
$ echo "こんにちは、私の名前付きパイプ!" > / tmp / my_named_pipe
その結果、プログラムから次の出力が表示されます。
$ ./mkfifo / tmp / my_named_pipeが作成されます/ tmp / my_named_pipeが開かれます着信メッセージ(22):こんにちは、私の名前付きパイプです! 読む:成功

共有メモリ

次のタイプのプロセス間通信は共有メモリです( 共有メモリ)。 2つのプロセスによって同時にアクセスされる、メモリ内の特定の名前付き領域として概略的に説明します。


共有メモリを割り当てるには、POSIX関数を使用します shm_open():
#含む int shm_open(const char * name、int oflag、mode_t mode);
この関数は、メモリオブジェクトに関連付けられているファイル記述子を返します。 この記述子は、後で他の関数で使用できます(たとえば、 mmap()また mprotect()).

オブジェクトがデタッチ/削除されるまで、メモリオブジェクトの整合性は、それに関連付けられているすべてのデータを含めて保持されます( shm_unlink())。 これは、プロセスの1つを明示的に呼び出す限り、どのプロセスもメモリオブジェクトにアクセスできることを意味します(名前がわかっている場合)。 shm_unlink().

変数 oflag次のフラグのビット単位の「OR」です。

  • O_RDONLY-読み取り専用の権限で開く
  • O_RDWR-読み取りおよび書き込み権限で開く
  • O_CREAT-オブジェクトがすでに存在する場合、フラグは効果がありません。 それ以外の場合は、オブジェクトが作成され、モードに従ってアクセス権が設定されます。
  • O_EXCL-このフラグをO_CREATEと組み合わせて設定すると、共有メモリセグメントがすでに存在する場合、shm_openはエラーを返します。
パラメータ値の設定方法 モード前の段落「メッセージパッシング」で詳細に説明されています。

共有メモリオブジェクトを作成した後、を呼び出して共有メモリのサイズを設定します ftruncate()。 関数の入力で、オブジェクトのファイル記述子と必要なサイズ。

次のコードは、共有メモリの作成、変更、および削除を示しています。 また、共有メモリを作成した後、プログラムが終了する方法も示していますが、次にプログラムを起動すると、実行されるまでアクセスできます。 shm_unlink().
shm_open.c
#含む #含む #含む #含む #含む #含む #define SHARED_MEMORY_OBJECT_NAME "my_shared_memory" #define SHARED_MEMORY_OBJECT_SIZE 50 #define SHM_CREATE 1 #define SHM_PRINT 3 #define SHM_CLOSE 4 void(const char * s)(printf( "Usage:%s ["text"] \ n "、s);)int main(int argc、char ** argv)(int shm、len、cmd、mode = 0; char * addr; if(argc< 2) { usage(argv); return 1; } if ((!strcmp(argv, "create") || !strcmp(argv, "write")) && (argc == 3)) { len = strlen(argv); len = (len<=SHARED_MEMORY_OBJECT_SIZE)?len:SHARED_MEMORY_OBJECT_SIZE; mode = O_CREAT; cmd = SHM_CREATE; } else if (! strcmp(argv, "print")) { cmd = SHM_PRINT; } else if (! strcmp(argv, "unlink")) { cmd = SHM_CLOSE; } else { usage(argv); return 1; } if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode|O_RDWR, 0777)) == -1) { perror("shm_open"); return 1; } if (cmd == SHM_CREATE) { if (ftruncate(shm, SHARED_MEMORY_OBJECT_SIZE+1) == -1) { perror("ftruncate"); return 1; } } addr = mmap(0, SHARED_MEMORY_OBJECT_SIZE+1, PROT_WRITE|PROT_READ, MAP_SHARED, shm, 0); if (addr == (char*)-1) { perror("mmap"); return 1; } switch (cmd) { case SHM_CREATE: memcpy(addr, argv, len); addr = "\0"; printf("Shared memory filled in. You may run "%s print" to see value.\n", argv); break; case SHM_PRINT: printf("Got from shared memory: %s\n", addr); break; } munmap(addr, SHARED_MEMORY_OBJECT_SIZE); close(shm); if (cmd == SHM_CLOSE) { shm_unlink(SHARED_MEMORY_OBJECT_NAME); } return 0; } [скачать ]

メモリオブジェクトを作成した後、を呼び出して必要な共有メモリのサイズを設定します ftruncate()。 次に、共有メモリにアクセスしました mmap(). (一般的に言って、呼び出し自体でも mmap()共有メモリを作成できます。 しかし、通話の違い shm_open()コンピュータがアンインストールまたは再起動されるまで、メモリは割り当てられたままになります。)

今回は、オプションを使用してコードをコンパイルする必要があります -lrt:
$ gcc -o shm_open -lrt shm_open.c
何が起きたのか見てみましょう:
$ ./shm_open create "こんにちは、私の共有メモリ!" 共有メモリが入力されました。 「./shm_openprint」を実行して値を確認できます。 $ ./shm_open print共有メモリから取得:こんにちは、私の共有メモリです! $ ./shm_open create "Hello!" 共有メモリが入力されました。 「./shm_openprint」を実行して値を確認できます。 $ ./shm_open print共有メモリから取得:こんにちは! $ ./shm_open close $ ./shm_open print shm_open:そのようなファイルやディレクトリはありません
プログラムでは、共有メモリの作成とその内容の変更の両方に「create」引数を使用します。

メモリオブジェクトの名前がわかれば、共有メモリの内容を変更できます。 しかし、私たちは呼び出す必要があります shm_unlink()記憶が私たちに利用できなくなる方法と shm_open()パラメータなし O_CREATE「そのようなファイルまたはディレクトリはありません」というエラーを返します。

セマフォ

セマフォは、スレッドを同期し、複数のスレッド/プロセスによる共有メモリ(グローバル変数など)への同時アクセスを制御するために最も一般的に使用される方法です。 セマフォの場合のプロセス間の相互作用は、プロセスが同じデータセットで動作し、このデータに応じて動作を調整することです。

セマフォには次の2つのタイプがあります。

  1. カウンター付きのセマフォ(セマフォをカウント)。これにより、プロセスにアクセスするプロセスのリソース制限が決定されます。
  2. 2つの状態「0」または「1」を持つバイナリセマフォ(多くの場合、「ビジー」または「非ビジー」)
両方のタイプのセマフォを検討してください。

カウンター付きセマフォ

カウンターを備えたセマフォのポイントは、特定の数のプロセスにのみ一部のリソースへのアクセスを許可することです。 リソースが解放されると、残りはキューで待機します。

したがって、セマフォを実装するには、POSIX関数を使用します sem_open():
#含む sem_t * sem_open(const char * name、int oflag、mode_t mode、unsigned int value);
セマフォを作成する関数では、特定のルールに従って作成されたセマフォの名前と制御フラグを渡します。 したがって、名前付きセマフォを取得します。
セマフォの名前は次のように構成されています。最初に「/」(スラッシュ)があり、その後にラテン文字が続きます。 スラッシュ文字は使用しないでください。 セマフォ名の長さは最大251文字です。

セマフォを作成する必要がある場合は、制御フラグが渡されます O_CREATE。 既存のセマフォの使用を開始するには、 oflagゼロに等しい。 旗と一緒なら O_CREATE旗を渡す O_EXCL、次に関数 sem_open()指定された名前のセマフォがすでに存在する場合、エラーを返します。

パラメータ モード前の章で説明したのと同じ方法で権限を設定します。 変数 価値セマフォの初期値が初期化されます。 両方のオプション モード価値指定された名前のセマフォがすでに存在する場合は無視され、 sem_open()旗と一緒に呼ばれる O_CREATE.

既存のセマフォをすばやく開くには、次の構造を使用します。
#含む sem_t * sem_open(const char * name、int oflag); 、セマフォ名と制御フラグのみが指定されています。

カウンター付きのセマフォの例

セマフォを使用してプロセスを同期する例を考えてみましょう。 この例では、1つのプロセスがセマフォの値をインクリメントし、2番目のプロセスがセマフォをリセットしてさらに実行を続行するのを待ちます。
sem_open.c
#含む #含む #含む #含む #define SEMAPHORE_NAME "/ my_named_semaphore" int main(int argc、char ** argv)(sem_t * sem; if(argc == 2)(printf( "Dropping semaphore ... \ n"); if((sem = sem_open (SEMAPHORE_NAME、0))== SEM_FAILED)(perror( "sem_open"); return 1;)sem_post(sem); perror( "sem_post"); printf( "Semaphoredropped。\ n"); return 0;)if ((sem = sem_open(SEMAPHORE_NAME、O_CREAT、0777、0))== SEM_FAILED)(perror( "sem_open"); return 1;)printf( "セマフォが取得されます。\ nドロップされるのを待っています。\ n") ; if(sem_wait(sem)< 0) perror("sem_wait"); if (sem_close(sem) < 0) perror("sem_close"); return 0; } [скачать ]

1つのコンソールで実行:
$。/ sem_openセマフォが取得されます。 ドロップされるのを待っています。<-- здесь процесс в ожидании другого процесса sem_wait: Success sem_close: Success
隣接するコンソールで、次を実行します。
$ ./sem_open1セマフォを削除しています... sem_post:成功セマフォが削除されました。

バイナリセマフォ

sem_open関数も使用されるバイナリセマフォの代わりに、「ミューテックス」(mutex)と呼ばれるはるかに一般的に使用されるセマフォを検討します。

ミューテックスは、基本的にバイナリセマフォ(つまり、占有状態と非占有状態の2つの状態を持つセマフォ)と同じです。 ただし、「ミューテックス」という用語は、2つのプロセスがデータ/変数を同時に共有できないようにするスキームを説明するためによく使用されます。 「バイナリセマフォ」という用語は、単一のリソースへのアクセスを制限する構造を説明するためによく使用されます。 つまり、バイナリセマフォは、1つのプロセスがセマフォを「占有」し、別のプロセスがセマフォを「解放」する場合に使用されます。 ミューテックスは、それを占有していたのと同じプロセス/スレッドによって解放されます。

たとえば、多くのクライアントがアクセスできるデータベースなど、書面でのミューテックスなしでは実行できません。

ミューテックスを使用するには、pthread_mutex_init()関数を呼び出す必要があります。
#含む int pthread_mutex_init(pthread_mutex_t * mutex、const pthread_mutexattr_t * mutexattr);
この関数はミューテックス(変数)を初期化します ミューテックス) 属性 mutexattr。 もしも mutexattr等しい ヌル、次にミューテックスがデフォルト値に初期化されます。 関数(戻りコード0)が正常に実行された場合、ミューテックスは初期化されて「フリー」であると見なされます。

発生する可能性のある一般的なエラー:

  • EAGAIN-ミューテックスを初期化するために必要なリソース(メモリ以外)が不足しています
  • ENOMEM- メモリーが充分ではありません
  • EPERM-操作を実行する権利はありません
  • EBUSY-すでに初期化されているが破棄されていないミューテックスの初期化を試みます
  • EINVAL- 意味 mutexattr有効ではありません
ミューテックスを取得または解放するには、次の関数を使用します。
int pthread_mutex_lock(pthread_mutex_t * mutex); int pthread_mutex_trylock(pthread_mutex_t * mutex); int pthread_mutex_unlock(pthread_mutex_t * mutex);
関数 pthread_mutex_lock()、 もしも ミューテックスまだ占領されていない、それからそれを占領し、その所有者になり、すぐに去ります。 ミューテックスが占有されている場合、プロセスのそれ以上の実行をブロックし、ミューテックスが解放されるのを待ちます。
関数 pthread_mutex_trylock()関数の動作は同じです pthread_mutex_lock()、1つの例外を除いて、次の場合はプロセスをブロックしません ミューテックス忙しいが戻ってきます EBUSYコード。
関数 pthread_mutex_unlock()占有されていたミューテックスを解放します。

のリターンコード pthread_mutex_lock():

  • EINVAL-ミューテックスが正しく初期化されていません
  • EDEADLK-ミューテックスはすでに現在のプロセスによって占有されています
のリターンコード pthread_mutex_trylock():
  • EBUSY-ミューテックスはすでにビジーです
のリターンコード pthread_mutex_unlock():
  • EINVAL-ミューテックスが正しく初期化されていません
  • EPERM-呼び出しプロセスはミューテックスを所有していません

ミューテックスの例

mutex.c
#含む #含む #含む #含む staticintカウンター; //共有リソースstaticpthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void incr_counter(void * p)(do(usleep(10); //ミューテックスロックの間にタイムスライスを設定しましょうpthread_mutex_lock(&mutex); counter ++; printf( "%d \ n"、counter); sleep(1) ; pthread_mutex_unlock(&mutex);)while(1);)void reset_counter(void * p)(char buf; int num = 0; int rc; pthread_mutex_lock(&mutex); //メッセージを表示するためだけにミューテックスをブロックするprintf( "Enter the番号を付け、「Enter」を押して、いつでも新しい値でカウンターを初期化します。\ n "); sleep(3); pthread_mutex_unlock(&mutex); //ブロックされたミューテックスのブロックを解除して、別のスレッドが機能するようにします(if(gets(buf)!= buf)return; //ばかげた保護なし!オーバーフローのリスク!num = atoi(buf); if((rc = pthread_mutex_trylock(&mutex))== EBUSY)(printf( "Mutexはすでに別のプロセスによってロックされています。\ nLet "s lock mutex using pthread_mutex_lock()。\ n"); pthread_mutex_lock(&mutex);)else if(rc == 0)(printf( "WOW!You are on time!Congratulation!\ n");)else(printf ( "エラー:%d \ n"、rc); return;)counter = num; printf( "カウンターiの新しい値 s%d \ n "、カウンター); pthread_mutex_unlock(&mutex); )while(1); )int main(int argc、char ** argv)(pthread_t thread_1; pthread_t thread_2; counter = 0; pthread_create(&thread_1、NULL、(void *)&incr_counter、NULL); pthread_create(&thread_2、NULL、(void *)&reset_counter、 NULL); pthread_join(thread_2、NULL); return 0;)[ダウンロード]

この例は、2つのスレッドが共通の変数を共有する方法を示しています。 自動モードの1つのスレッド(最初のスレッド)は、変数を常にインクリメントします カウンターこの変数を1秒間占有している間、1つずつ。 この最初のスレッドは、変数への2番目のアクセスを提供します カウント 10ミリ秒だけ、それから1秒間それを取り戻します。 2番目のスレッドでは、端末から変数の新しい値を入力するように求められます。

「ミューテックス」テクノロジーを使用しなかった場合、2つのスレッドによる同時アクセスで、グローバル変数にどのような値が含まれるかはわかりません。 また、打ち上げ時の違い pthread_mutex_lock()pthread_mutex_trylock().

追加のパラメータを使用してコードをコンパイルする必要があります -lpthread:
$ gcc -o mutex -lpthread mutex.c
ターミナルウィンドウに新しい値を入力するだけで、変数の値を実行および変更します。
$ ./mutex数値を入力し、「Enter」を押して、いつでも新しい値でカウンターを初期化します。 1 2 3 30 <--- новое значение переменной Mutex is already locked by another process. Let"s lock mutex using pthread_mutex_lock(). New value for counter is 30 31 32 33 1 <--- новое значение переменной Mutex is already locked by another process. Let"s lock mutex using pthread_mutex_lock(). New value for counter is 1 2 3

結論の代わりに

次の記事では、d-busとRPCテクノロジーについて見ていきたいと思います。 興味があれば教えてください。
ありがとう。

UPD:セマフォに関する第3章を更新しました。 ミューテックスに関するサブチャプターを追加しました。

タグ:タグを追加

プロセス間通信( プロセス間通信(IPC))は、プロセススレッド間でデータを交換するための一連のメソッドです。 プロセスは、同じコンピューターとネットワークで接続された異なるコンピューターの両方で起動できます。 IPCには、「シグナル」、「ソケット」、「セマフォ」、「ファイル」、「メッセージ」など、いくつかのタイプがあります。

この記事では、3種類のIPCのみを検討します。

余談:この記事は教育的なものであり、システムプログラミングの道を歩み始めたばかりの人々を対象としています。 その主なアイデアは、POSIX準拠のOS上のプロセス間のさまざまな相互作用の方法に精通することです。

名前付きパイプ

ソケット、チャネル、Dバス、およびその他のテクノロジーを使用してメッセージを送信できます。 隅々にあるソケットについて読むことができますが、D-busについては別の記事を書いてください。 そのため、POSIX標準に適合し、実用的な例を示す、目立たないテクノロジに焦点を当てることにしました。

名前付きパイプを介してメッセージを渡すことを検討してください。 概略的には、転送は次のようになります。

名前付きパイプを作成するには、関数を使用します。 mkfifo():
#含む int mkfifo(const char * pathname、mode_t mode);
この関数は、という名前の特別なFIFOファイルを作成します パス名、およびパラメータ モードファイルのアクセス許可を設定します。

ノート: モード現在の値と組み合わせて使用 umask次のように: (モード&〜umask)。 この操作の結果は新しい値になります umask作成しているファイルの場合。 このため、0777( S_IRWXO | S_IRWXG | S_IRWXU)現在のマスクのビットを上書きしないようにします。
ファイルが作成されると、どのプロセスでも、通常のファイルを開くのと同じ方法で、読み取りまたは書き込みのためにファイルを開くことができます。 ただし、ファイルを正しく使用するには、2つのプロセス/スレッドで同時に開く必要があります。1つはデータの受信(ファイルの読み取り)用、もう1つは送信(ファイルへの書き込み)用です。

FIFOファイルの作成に成功した場合、 mkfifo() 0(ゼロ)を返します。 エラーが発生した場合、関数は-1を返し、エラーコードを変数に設定します errno.

チャネルの作成中に発生する可能性のある一般的なエラー:

  • EACCES-パス内のディレクトリの1つで実行(実行)する権限がありません パス名
  • 存在- ファイル パス名ファイルがシンボリックリンクであっても、すでに存在します
  • ENOENT-で言及されているディレクトリはありません パス名、または壊れたリンクです
  • ENOSPC-新しいファイルを作成するスペースがありません
  • ENOTDIR-で言及されているディレクトリの1つ パス名、実際にはそうではありません
  • EROFS-読み取り専用ファイルシステムでFIFOファイルを作成しようとします
作成したファイルの読み取りと書き込みは、関数を使用して実行されます 読む()書きます().

mkfifo.c
#含む #含む #含む #含む #define NAMEDPIPE_NAME "/ tmp / my_named_pipe" #define BUFSIZE 50 int main(int argc、char ** argv)(int fd、len; char buf; if(mkfifo(NAMEDPIPE_NAME、0777))(perror( "mkfifo"); return 1;)printf( "%s is created \ n"、NAMEDPIPE_NAME); if((fd = open(NAMEDPIPE_NAME、O_RDONLY))<= 0) { perror("open"); return 1; } printf("%s is opened\n", NAMEDPIPE_NAME); do { memset(buf, "\0", BUFSIZE); if ((len = read(fd, buf, BUFSIZE-1)) <= 0) { perror("read"); close(fd); remove(NAMEDPIPE_NAME); return 0; } printf("Incomming message (%d): %s\n", len, buf); } while (1); } [скачать ]

ファイルを読み取り専用で開きます( O_RDONLY)。 そして使用することができます O_NONBLOCK反対側が書き込みのためにファイルを開くのを待たないように、FIFOファイル用に特別に設計された修飾子。 しかし、上記のコードでは、この方法は不便です。

プログラムをコンパイルしてから実行します。
$ gcc -o mkfifo mkfifo.c $ ./mkfifo
次のターミナルウィンドウで、次を実行します。
$ echo "こんにちは、私の名前付きパイプ!" > / tmp / my_named_pipe
その結果、プログラムから次の出力が表示されます。
$ ./mkfifo / tmp / my_named_pipeが作成されます/ tmp / my_named_pipeが開かれます着信メッセージ(22):こんにちは、私の名前付きパイプです! 読む:成功

共有メモリ

次のタイプのプロセス間通信は共有メモリです( 共有メモリ)。 2つのプロセスによって同時にアクセスされる、メモリ内の特定の名前付き領域として概略的に説明します。


共有メモリを割り当てるには、POSIX関数を使用します shm_open():
#含む int shm_open(const char * name、int oflag、mode_t mode);
この関数は、メモリオブジェクトに関連付けられているファイル記述子を返します。 この記述子は、後で他の関数で使用できます(たとえば、 mmap()また mprotect()).

オブジェクトがデタッチ/削除されるまで、メモリオブジェクトの整合性は、それに関連付けられているすべてのデータを含めて保持されます( shm_unlink())。 これは、プロセスの1つを明示的に呼び出す限り、どのプロセスもメモリオブジェクトにアクセスできることを意味します(名前がわかっている場合)。 shm_unlink().

変数 oflag次のフラグのビット単位の「OR」です。

  • O_RDONLY-読み取り専用の権限で開く
  • O_RDWR-読み取りおよび書き込み権限で開く
  • O_CREAT-オブジェクトがすでに存在する場合、フラグは効果がありません。 それ以外の場合は、オブジェクトが作成され、モードに従ってアクセス権が設定されます。
  • O_EXCL-このフラグをO_CREATEと組み合わせて設定すると、共有メモリセグメントがすでに存在する場合、shm_openはエラーを返します。
パラメータ値の設定方法 モード前の段落「メッセージパッシング」で詳細に説明されています。

共有メモリオブジェクトを作成した後、を呼び出して共有メモリのサイズを設定します ftruncate()。 関数の入力で、オブジェクトのファイル記述子と必要なサイズ。

次のコードは、共有メモリの作成、変更、および削除を示しています。 また、共有メモリを作成した後、プログラムが終了する方法も示していますが、次にプログラムを起動すると、実行されるまでアクセスできます。 shm_unlink().
shm_open.c
#含む #含む #含む #含む #含む #含む #define SHARED_MEMORY_OBJECT_NAME "my_shared_memory" #define SHARED_MEMORY_OBJECT_SIZE 50 #define SHM_CREATE 1 #define SHM_PRINT 3 #define SHM_CLOSE 4 void(const char * s)(printf( "Usage:%s ["text"] \ n "、s);)int main(int argc、char ** argv)(int shm、len、cmd、mode = 0; char * addr; if(argc< 2) { usage(argv); return 1; } if ((!strcmp(argv, "create") || !strcmp(argv, "write")) && (argc == 3)) { len = strlen(argv); len = (len<=SHARED_MEMORY_OBJECT_SIZE)?len:SHARED_MEMORY_OBJECT_SIZE; mode = O_CREAT; cmd = SHM_CREATE; } else if (! strcmp(argv, "print")) { cmd = SHM_PRINT; } else if (! strcmp(argv, "unlink")) { cmd = SHM_CLOSE; } else { usage(argv); return 1; } if ((shm = shm_open(SHARED_MEMORY_OBJECT_NAME, mode|O_RDWR, 0777)) == -1) { perror("shm_open"); return 1; } if (cmd == SHM_CREATE) { if (ftruncate(shm, SHARED_MEMORY_OBJECT_SIZE+1) == -1) { perror("ftruncate"); return 1; } } addr = mmap(0, SHARED_MEMORY_OBJECT_SIZE+1, PROT_WRITE|PROT_READ, MAP_SHARED, shm, 0); if (addr == (char*)-1) { perror("mmap"); return 1; } switch (cmd) { case SHM_CREATE: memcpy(addr, argv, len); addr = "\0"; printf("Shared memory filled in. You may run "%s print" to see value.\n", argv); break; case SHM_PRINT: printf("Got from shared memory: %s\n", addr); break; } munmap(addr, SHARED_MEMORY_OBJECT_SIZE); close(shm); if (cmd == SHM_CLOSE) { shm_unlink(SHARED_MEMORY_OBJECT_NAME); } return 0; } [скачать ]

メモリオブジェクトを作成した後、を呼び出して必要な共有メモリのサイズを設定します ftruncate()。 次に、共有メモリにアクセスしました mmap(). (一般的に言って、呼び出し自体でも mmap()共有メモリを作成できます。 しかし、通話の違い shm_open()コンピュータがアンインストールまたは再起動されるまで、メモリは割り当てられたままになります。)

今回は、オプションを使用してコードをコンパイルする必要があります -lrt:
$ gcc -o shm_open -lrt shm_open.c
何が起きたのか見てみましょう:
$ ./shm_open create "こんにちは、私の共有メモリ!" 共有メモリが入力されました。 「./shm_openprint」を実行して値を確認できます。 $ ./shm_open print共有メモリから取得:こんにちは、私の共有メモリです! $ ./shm_open create "Hello!" 共有メモリが入力されました。 「./shm_openprint」を実行して値を確認できます。 $ ./shm_open print共有メモリから取得:こんにちは! $ ./shm_open close $ ./shm_open print shm_open:そのようなファイルやディレクトリはありません
プログラムでは、共有メモリの作成とその内容の変更の両方に「create」引数を使用します。

メモリオブジェクトの名前がわかれば、共有メモリの内容を変更できます。 しかし、私たちは呼び出す必要があります shm_unlink()記憶が私たちに利用できなくなる方法と shm_open()パラメータなし O_CREATE「そのようなファイルまたはディレクトリはありません」というエラーを返します。

セマフォ

セマフォは、スレッドを同期し、複数のスレッド/プロセスによる共有メモリ(グローバル変数など)への同時アクセスを制御するために最も一般的に使用される方法です。 セマフォの場合のプロセス間の相互作用は、プロセスが同じデータセットで動作し、このデータに応じて動作を調整することです。

セマフォには次の2つのタイプがあります。

  1. カウンター付きのセマフォ(セマフォをカウント)。これにより、プロセスにアクセスするプロセスのリソース制限が決定されます。
  2. 2つの状態「0」または「1」を持つバイナリセマフォ(多くの場合、「ビジー」または「非ビジー」)
両方のタイプのセマフォを検討してください。

カウンター付きセマフォ

カウンターを備えたセマフォのポイントは、特定の数のプロセスにのみ一部のリソースへのアクセスを許可することです。 リソースが解放されると、残りはキューで待機します。

したがって、セマフォを実装するには、POSIX関数を使用します sem_open():
#含む sem_t * sem_open(const char * name、int oflag、mode_t mode、unsigned int value);
セマフォを作成する関数では、特定のルールに従って作成されたセマフォの名前と制御フラグを渡します。 したがって、名前付きセマフォを取得します。
セマフォの名前は次のように構成されています。最初に「/」(スラッシュ)があり、その後にラテン文字が続きます。 スラッシュ文字は使用しないでください。 セマフォ名の長さは最大251文字です。

セマフォを作成する必要がある場合は、制御フラグが渡されます O_CREATE。 既存のセマフォの使用を開始するには、 oflagゼロに等しい。 旗と一緒なら O_CREATE旗を渡す O_EXCL、次に関数 sem_open()指定された名前のセマフォがすでに存在する場合、エラーを返します。

パラメータ モード前の章で説明したのと同じ方法で権限を設定します。 変数 価値セマフォの初期値が初期化されます。 両方のオプション モード価値指定された名前のセマフォがすでに存在する場合は無視され、 sem_open()旗と一緒に呼ばれる O_CREATE.

既存のセマフォをすばやく開くには、次の構造を使用します。
#含む sem_t * sem_open(const char * name、int oflag); 、セマフォ名と制御フラグのみが指定されています。

カウンター付きのセマフォの例

セマフォを使用してプロセスを同期する例を考えてみましょう。 この例では、1つのプロセスがセマフォの値をインクリメントし、2番目のプロセスがセマフォをリセットしてさらに実行を続行するのを待ちます。
sem_open.c
#含む #含む #含む #含む #define SEMAPHORE_NAME "/ my_named_semaphore" int main(int argc、char ** argv)(sem_t * sem; if(argc == 2)(printf( "Dropping semaphore ... \ n"); if((sem = sem_open (SEMAPHORE_NAME、0))== SEM_FAILED)(perror( "sem_open"); return 1;)sem_post(sem); perror( "sem_post"); printf( "Semaphoredropped。\ n"); return 0;)if ((sem = sem_open(SEMAPHORE_NAME、O_CREAT、0777、0))== SEM_FAILED)(perror( "sem_open"); return 1;)printf( "セマフォが取得されます。\ nドロップされるのを待っています。\ n") ; if(sem_wait(sem)< 0) perror("sem_wait"); if (sem_close(sem) < 0) perror("sem_close"); return 0; } [скачать ]

1つのコンソールで実行:
$。/ sem_openセマフォが取得されます。 ドロップされるのを待っています。<-- здесь процесс в ожидании другого процесса sem_wait: Success sem_close: Success
隣接するコンソールで、次を実行します。
$ ./sem_open1セマフォを削除しています... sem_post:成功セマフォが削除されました。

バイナリセマフォ

sem_open関数も使用されるバイナリセマフォの代わりに、「ミューテックス」(mutex)と呼ばれるはるかに一般的に使用されるセマフォを検討します。

ミューテックスは、基本的にバイナリセマフォ(つまり、占有状態と非占有状態の2つの状態を持つセマフォ)と同じです。 ただし、「ミューテックス」という用語は、2つのプロセスがデータ/変数を同時に共有できないようにするスキームを説明するためによく使用されます。 「バイナリセマフォ」という用語は、単一のリソースへのアクセスを制限する構造を説明するためによく使用されます。 つまり、バイナリセマフォは、1つのプロセスがセマフォを「占有」し、別のプロセスがセマフォを「解放」する場合に使用されます。 ミューテックスは、それを占有していたのと同じプロセス/スレッドによって解放されます。

たとえば、多くのクライアントがアクセスできるデータベースなど、書面でのミューテックスなしでは実行できません。

ミューテックスを使用するには、pthread_mutex_init()関数を呼び出す必要があります。
#含む int pthread_mutex_init(pthread_mutex_t * mutex、const pthread_mutexattr_t * mutexattr);
この関数はミューテックス(変数)を初期化します ミューテックス) 属性 mutexattr。 もしも mutexattr等しい ヌル、次にミューテックスがデフォルト値に初期化されます。 関数(戻りコード0)が正常に実行された場合、ミューテックスは初期化されて「フリー」であると見なされます。

発生する可能性のある一般的なエラー:

  • EAGAIN-ミューテックスを初期化するために必要なリソース(メモリ以外)が不足しています
  • ENOMEM- メモリーが充分ではありません
  • EPERM-操作を実行する権利はありません
  • EBUSY-すでに初期化されているが破棄されていないミューテックスの初期化を試みます
  • EINVAL- 意味 mutexattr有効ではありません
ミューテックスを取得または解放するには、次の関数を使用します。
int pthread_mutex_lock(pthread_mutex_t * mutex); int pthread_mutex_trylock(pthread_mutex_t * mutex); int pthread_mutex_unlock(pthread_mutex_t * mutex);
関数 pthread_mutex_lock()、 もしも ミューテックスまだ占領されていない、それからそれを占領し、その所有者になり、すぐに去ります。 ミューテックスが占有されている場合、プロセスのそれ以上の実行をブロックし、ミューテックスが解放されるのを待ちます。
関数 pthread_mutex_trylock()関数の動作は同じです pthread_mutex_lock()、1つの例外を除いて、次の場合はプロセスをブロックしません ミューテックス忙しいが戻ってきます EBUSYコード。
関数 pthread_mutex_unlock()占有されていたミューテックスを解放します。

のリターンコード pthread_mutex_lock():

  • EINVAL-ミューテックスが正しく初期化されていません
  • EDEADLK-ミューテックスはすでに現在のプロセスによって占有されています
のリターンコード pthread_mutex_trylock():
  • EBUSY-ミューテックスはすでにビジーです
のリターンコード pthread_mutex_unlock():
  • EINVAL-ミューテックスが正しく初期化されていません
  • EPERM-呼び出しプロセスはミューテックスを所有していません

ミューテックスの例

mutex.c
#含む #含む #含む #含む staticintカウンター; //共有リソースstaticpthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void incr_counter(void * p)(do(usleep(10); //ミューテックスロックの間にタイムスライスを設定しましょうpthread_mutex_lock(&mutex); counter ++; printf( "%d \ n"、counter); sleep(1) ; pthread_mutex_unlock(&mutex);)while(1);)void reset_counter(void * p)(char buf; int num = 0; int rc; pthread_mutex_lock(&mutex); //メッセージを表示するためだけにミューテックスをブロックするprintf( "Enter the番号を付け、「Enter」を押して、いつでも新しい値でカウンターを初期化します。\ n "); sleep(3); pthread_mutex_unlock(&mutex); //ブロックされたミューテックスのブロックを解除して、別のスレッドが機能するようにします(if(gets(buf)!= buf)return; //ばかげた保護なし!オーバーフローのリスク!num = atoi(buf); if((rc = pthread_mutex_trylock(&mutex))== EBUSY)(printf( "Mutexはすでに別のプロセスによってロックされています。\ nLet "s lock mutex using pthread_mutex_lock()。\ n"); pthread_mutex_lock(&mutex);)else if(rc == 0)(printf( "WOW!You are on time!Congratulation!\ n");)else(printf ( "エラー:%d \ n"、rc); return;)counter = num; printf( "カウンターiの新しい値 s%d \ n "、カウンター); pthread_mutex_unlock(&mutex); )while(1); )int main(int argc、char ** argv)(pthread_t thread_1; pthread_t thread_2; counter = 0; pthread_create(&thread_1、NULL、(void *)&incr_counter、NULL); pthread_create(&thread_2、NULL、(void *)&reset_counter、 NULL); pthread_join(thread_2、NULL); return 0;)[ダウンロード]

この例は、2つのスレッドが共通の変数を共有する方法を示しています。 自動モードの1つのスレッド(最初のスレッド)は、変数を常にインクリメントします カウンターこの変数を1秒間占有している間、1つずつ。 この最初のスレッドは、変数への2番目のアクセスを提供します カウント 10ミリ秒だけ、それから1秒間それを取り戻します。 2番目のスレッドでは、端末から変数の新しい値を入力するように求められます。

「ミューテックス」テクノロジーを使用しなかった場合、2つのスレッドによる同時アクセスで、グローバル変数にどのような値が含まれるかはわかりません。 また、打ち上げ時の違い pthread_mutex_lock()pthread_mutex_trylock().

追加のパラメータを使用してコードをコンパイルする必要があります -lpthread:
$ gcc -o mutex -lpthread mutex.c
ターミナルウィンドウに新しい値を入力するだけで、変数の値を実行および変更します。
$ ./mutex数値を入力し、「Enter」を押して、いつでも新しい値でカウンターを初期化します。 1 2 3 30 <--- новое значение переменной Mutex is already locked by another process. Let"s lock mutex using pthread_mutex_lock(). New value for counter is 30 31 32 33 1 <--- новое значение переменной Mutex is already locked by another process. Let"s lock mutex using pthread_mutex_lock(). New value for counter is 1 2 3

結論の代わりに

次の記事では、d-busとRPCテクノロジーについて見ていきたいと思います。 興味があれば教えてください。
ありがとう。

UPD:セマフォに関する第3章を更新しました。 ミューテックスに関するサブチャプターを追加しました。

タグ:

  • Linux
  • posix
  • ipc
  • プログラミング
タグを追加する