🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

排他制御

排他制御とは、特定のファイル・データへのアクセスや更新を制御することです。特にファイルやデータベースへ書き込みを行う際、データの整合性を保つため別のプログラムによる書き込みを一時的に制御することを指します。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

2回答

4334閲覧

共有メモリを使ったメッセージの送受信

Timosy

総合スコア3

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

排他制御

排他制御とは、特定のファイル・データへのアクセスや更新を制御することです。特にファイルやデータベースへ書き込みを行う際、データの整合性を保つため別のプログラムによる書き込みを一時的に制御することを指します。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

0クリップ

投稿2021/01/15 15:32

#質問
共有メモリを確保して,2つのプログラムを用いてメッセージを送受信するシステムを考えていますが同じメッセージを何度も受信してしまうことがあり解決策をさがしています.
コーディングはLinuxです.

よい排他制御のコーディングの仕方などがあればお教えください.

#コード
###send.c

Linux

1#include <stdio.h> 2#include <sys/types.h> 3#include <sys/ipc.h> 4#include <sys/shm.h> 5#include <stdlib.h> 6#include <string.h> 7 8int main (int argc, char *argv[]) 9{ 10 int id; 11 char *adr; 12 13 if (argc !=2) 14 { 15 fprintf(stderr, "Usage: send-by-shm <Shared memory ID>"); 16 exit (EXIT_FAILURE); 17 } 18 19 id=atoi(argv[1]); 20 if((int) (adr = (char *)shmat (id, (void *) 0, 0)) == -1) 21 { 22 perror("shmat"); 23 exit(EXIT_FAILURE); 24 } 25 else 26 { 27 while(1) 28 { 29 printf("Input any string>"); 30 fgets(adr, 512, stdin); 31 adr[strlen(adr)-1] = '\0'; 32 if(strcmp (adr, "quit") == 0) 33 break; 34 } 35 if (shmdt (adr) == -1) 36 { 37 perror("shmdt"); 38 exit (EXIT_FAILURE); 39 } 40 } 41} 42

###receive.c

Linux

1#include <stdio.h> 2#include <sys/types.h> 3#include <sys/ipc.h> 4#include <sys/shm.h> 5#include <stdlib.h> 6#include <string.h> 7 8#define BUFSIZE 512 9 10int main() 11{ 12 int id; 13 char *adr; 14 15 if ((id = shmget (IPC_PRIVATE, BUFSIZE, IPC_CREAT|0666)) == -1) 16 { 17 perror ("shmget"); 18 exit(EXIT_FAILURE); 19 } 20 printf("Shared memory ID =%d\n", id); 21 22 if ((int)(adr=(char *) shmat (id, (void *) 0, 0)) == -1) 23 { 24 perror ("shmat"); 25 shmctl(id, IPC_RMID, NULL); 26 exit (EXIT_FAILURE); 27 } 28 else 29 { 30 strcpy (adr, "Initial"); 31 while(1) 32 { 33 printf("%s\n", adr); 34 if (strcmp (adr, "quit") == 0) { 35 break; 36 } 37 sleep(3); 38 } 39 40 if (shmdt (adr) == -1) 41 { 42 perror("shmdt"); 43 shmctl(id, IPC_RMID, NULL); 44 exit (EXIT_FAILURE); 45 } 46 } 47 48 if(shmctl(id, IPC_RMID, NULL) == -1) 49 { 50 perror("chmctl"); 51 exit(EXIT_FAILURE); 52 } 53} 54

#実行結果

bash-4.2$ gcc send.c bash-4.2$ ./a.out Usage: send <Shared memory ID>bash-4.2$ gcc receive.c bash-4.2$ ./a.out Shared memory ID = 753669 Initial Initial Initial Initial Initial Initial Initial Initial ...

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

「読んだかどうか」フラグが無いので、何度も読むのは当たり前です。

1つのメモリ領域を使った通信が一方方向であれば、排他制御は要りません。

・フラグの初期値0
・書く側で、データを書いたときにフラグに1を入れる
・読む側では、フラグが0なら何もせずsleepし、1ならデータを読んで0を入れておく

で、良いと思います。

あと、書く側で、一旦fgetsで改行付きのデータをadrに読み込んでから改行を削っていますので、
改行を削ってからフラグに1を入れます。でないと、改行付きのデータが読まれるかも。
そもそも、データを改行付きにすれば良いと思いますけど。

C

1int main (int argc, char *argv[]) 2{ 3 int id; 4 char *adr, *data; 5 中略 6 data = adr+1; 7 while(1) 8 { 9 printf("Input any string>"); 10 fgets(data, 511, stdin); 11 *adr = 1; 12 if(strcmp (data, "quit\n") == 0) { 13 break; 14 } 15 } 16 後略

C

1int main() 2{ 3 int id; 4 char *adr, *data; 5 中略 6 data = adr + 1; 7 *adr = 0; 8 while(1) 9 { 10 if(*adr){ 11 printf("%s", data); 12 if (strcmp (data, "quit\n") == 0) { 13 break; 14 } 15 *adr = 0; 16 } 17 sleep(3); 18 } 19 後略

書く側で「まだ読まれてなければ次のデータを入力しない」という処理を入れたければ入れる。

投稿2021/01/15 16:11

otn

総合スコア85893

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

angel_p_57

2021/01/16 04:03

> 1つのメモリ領域を使った通信が一方方向であれば、排他制御は要りません。 そんなわけないじゃないですか。 それと sleep() でポーリングするなら、わざわざ共有メモリ使う意味がありません。
otn

2021/01/16 06:49

データの抜け防止の要件は無いので、いいのでは?
退会済みユーザー

退会済みユーザー

2021/01/16 17:47

実現手段としてはさほど問題ないと思いますよ。 1バイトの読み書きが競合するのが気持ち悪いという話であれば、send側もフラグ0を見た上でstdatomic.hをincludeして_Atomic付き(C11)でフラグにアクセスすれば、sleepを削ってbusy loopにしても平気だと思います。 セマフォなどを使えばbusy loopも避けられますし、そちらの方が無駄がないですが、そもそも今の要件であれば、IPCとして共有メモリを使う理由が分からないというのが正直な感想で、とりあえず勉強したいだけであれば、まずはbusy loopからでいいような気がします(できればこの辺の理由を確認してからの回答がいいようにも思います)。
guest

0

一応「セマフォで」ということなので、SysVセマフォを想定して話します。
※他にも POSIXセマフォやPOSIX共有メモリというのもあり、機能的にはほぼ同じですが操作方法が違います

ただ、正直に言うと、共有メモリ+セマフォの真面目なサンプルを見たことがないので紹介できません。概念的にはこのページにあるような操作になるのですが。
http://ossforum.jp/en/node/883

具体的には、manを見て使い方を探るしかない気がします。
例えば semop(2)のmanページ には、「セマフォの値が0になるのを待って1に変える」というサンプルが載っています。
これは2操作同時の例なのでちょっと多いですが、1操作だけにして「セマフォの値が0なら共有メモリに書いてて良い、だから0でなければ0になるまで待つ、共有メモリへの書き込みが終わったら読んでいいことが分かるように1に変える)」という方法で、排他制御が可能です。
※読み込み側は逆に、1になるのを待ってから読み込み、終わったら0に変えるという感じ

なお、セマフォの初期化については注意が必要です。プログラム起動的に順序関係がある ( 起動コマンド順が、という意味ではなくて、片一方からもう一方を posix_spawn/fork+exec するような順序の意味 ) なら良いですが、どっちが初期化するべきか、もう初期化されているのか、判断する必要があるからです。
それについては semget(2)のmanページの「セマフォの初期化」を参考にどうぞ。

--

以下、セマフォ以外の話です。
もし、pthread(UNIX/Linuxでのマルチスレッドの仕組み)に慣れているなら「プロセス間で利用可能なミューテクス・条件変数」を使うのが楽かもしれません。ただし「慣れているなら」です。

で、ここら辺の話の敷居が高いと思うのであれば、排他制御を無理して頑張って共有メモリを使うのではなく、メッセージキュー、ソケット、名前付きパイプといった他の通信方法に切り替えても良いと思います。

投稿2021/01/16 04:20

編集2021/01/16 04:29
angel_p_57

総合スコア1681

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問