- ベストアンサー
共有メモリの同時アクセスについて教えてください。
2つのプロセスが同時に同じ共有メモリにアクセスすることは可能ですか? ミューテックスや、セマフォなどの排他制御を外せばできたりしませんか? 片方のプロセスで共有メモリから、データの一部をHDDに保存しつつ、それと同時に、もう片方のプロセスで共有メモリからデータの中身を表示させたいのですが・・・。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
読み込み同士なら排他制御しなくてもいいですけど。 けど、誰かその共有メモリに書き込むプロセスやスレッドがいるはずなので、 共有ロックは必要なのでは。
その他の回答 (3)
- 和泉 博(@hiroshi09s)
- ベストアンサー率54% (59/109)
冒頭のコメントにしたがって xyz.c および xyz2.c コンパイルし、./a.out で実行してください。 /* xyz.c with xyz2.c combination test program by gcc on Mac OSX * compile: gcc xyz.c * execution: ./a.out * loop end: "!" + [return] */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> #include <signal.h> #include <errno.h> #include "okw.h" #define CODE 999 // 適当に変更のこと #define OUT_FILE "data_list" // 〃 #define STDIN 0 #define STDERR 2 void handler(int); int main(void) { int shmid; void *shmaddr; int nread, n; char command_line[64]; /* 共有メモリを確保する */ if (((shmid = shmget(CODE, sizeof(*ptr) + 2, IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) || ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1)) { perror("Shared memory set"); exit(1); } /* 共有メモリに struct構造体をあてがう */ ptr = (struct set1 *)shmaddr; /* 自身のプロセスIdを記録する */ ptr->id_number[PARENT] = getpid(); /* シグナルハンドラー登録 */ signal(SIGCONT, handler); /* ↓ 手抜きです m(_ _)m * (OUT_FILEにバックグランドで追加書き込み可に) */ ptr->flag[PARENT] = ON; sprintf(command_line, "./xyz2 %d >>%s &", shmid, OUT_FILE); if (system(command_line) != 0) { perror("Shell command line"); exit(1); } /* プログラムサイズの大きな子プロセスの準備順延に配慮 */ fprintf(stderr, "Program setting now"); while (ptr->flag[PARENT] != OFF) { fprintf(stderr, "."); sleep(1); } fprintf(stderr, "\n\n"); do { n = 0; ptr->flag[OUTPUT] = OFF; while (1) { write(STDERR, "> ", 2); if ((nread = read(STDIN, &ptr->buffer[n], MEM_SIZE - n)) == -1) { perror("Read line"); exit(1); } if (ptr->buffer[n] == '!') { ptr->buffer[n] = '\0'; /* '!' を削除する */ break; } else n += nread; } /* 子プロセスへ入力結果をHDDに並列出力ようシグナルを送信 */ ptr->flag[PARENT] == OFF; kill(ptr->id_number[OUTPUT], SIGCONT); /* 結果のターミナル出力 */ printf("buffer:\n"); printf("%s", ptr->buffer); /* 作業継続の確認 */ write(STDERR, "> continue y/n? ", 16); read(STDIN, command_line, 2); /* HDD書き込み終了まで待機(dead_lockの回避) * 作業継続入力後も子プロセスが出力中なら1秒休む。 * ただし、出力が完了したならシグナル等で起こされる。 */ while (ptr->flag[PARENT] != OFF) sleep(1); } while (*command_line == 'y' || *command_line =='Y'); /* 子プロセスのプログラム終了送信 */ ptr->flag[OUTPUT] = ON; kill(ptr->id_number[OUTPUT], SIGCONT); /* 共有メモリを解放する */ if (shmdt(shmaddr) == -1 || shmctl(shmid, IPC_RMID, 0) == -1) { perror("Shared memory closed"); exit(1); } return 0; } /* 何もしないシグナル処理関数 */ void handler(int dummy) { ; } --- ここまで /* xyz2.c child process data output program by gcc on Mac OSX. * compile : gcc xyz2.c -o xyz2 * execution: シェルプロンプトから起動しないこと。 */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> #include <signal.h> #include <errno.h> #include "okw.h" void handler(int); int main(int argc, char *argv[]) { int shmid; void *shmaddr; /* 共有メモリを親プロセスから受け継ぐ */ shmid = atoi(argv[--argc]); if ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1) { perror("Shared menory set"); exit(1); } /* 共有メモリに struct構造体をあてがう */ ptr = (struct set1 *)shmaddr; /* 自身のプロセスIdを記録する */ ptr->id_number[OUTPUT] = getpid(); ptr->flag[OUTPUT] = OFF; /* シグナルハンドラー登録 */ signal(SIGCONT, handler); /* 親プロセスに「スタンバイ ok!」を知らせる */ ptr->flag[PARENT] = OFF; kill(ptr->id_number[PARENT], SIGCONT); while (1) { /* 親プロセスからのシグナルを待つ */ pause(); if (ptr->flag[OUTPUT] != OFF) break; /* 出力しないならループ脱出 */ else { /* 「出力中!」の看板を掲げ、出力する */ ptr->flag[PARENT] = ON; printf("%s", ptr->buffer); /* バッファリングせずに強制排出 */ fflush(stdout); /* 「出力中!」の看板を降ろし、親プロセスにシグナルを送付 */ ptr->flag[PARENT] = OFF; kill(ptr->id_number[PARENT], SIGCONT); } } /* 共有メモリを切り離す */ if (shmdt(shmaddr) == -1) { perror("Shared menory closed"); exit(1); } return 0; } /* 何もしないシグナル処理関数 */ void handler(int dummy) { ; } ---- ここまで /* 共通事項リストアップ・定義等ヘッダーファイル * file name: okw.h * * プロセス間での共通項を列挙し、不用意な間違いをなくす。 * そして、Cの線形プログラミング構造を積極的に活用する。 */ #define MEM_SIZE 512 // 共有メモリのサイズ #define PROCS 3 // 全プロセス数 #define OUTPUT 1 // 出力専門の子プロセス #define PARENT 0 // 親プロセス #define ON 1 #define OFF 0 struct set1 { char buffer[MEM_SIZE]; // data buffer signed char flag[PROCS]; // condition flag pid_t id_number[PROCS]; // process id code } *ptr;
- 和泉 博(@hiroshi09s)
- ベストアンサー率54% (59/109)
親プロセスのほうでデータを共有メモリに書き込み、表示しながら、子プロセスでは出力するプログラムを作成してみました。環境は Mac OSX です。 待機しているファイル出力プロセスにシグナルを送って同時に出力する場合は、kill()とpause()関数の間に出力プログラムを書けば良いと思います。書き込みは共有メモリに直接書き込んでいることから、子プロセスが書き出し完了まで待つようにしてみました。後は、これを応用すれば実現できると思います。 なお、スペースは2バイトスペースですので、半角に直して下さい。 /* xyz.c with xyz2.c combination test program by gcc on Mac OSX * execution: ./a.out * compile: gcc xyz2.c * loop end: input "!" */ #include <stdio.h> /* for printf() */ #include <unistd.h> /* for getpid() */ #include <stdlib.h> /* for exit() */ #include <sys/types.h> /* for shared memory */ #include <sys/ipc.h> /* 〃 */ #include <sys/shm.h> /* 〃 */ #include <sys/stat.h> #include <signal.h> /* for signal */ #define CODE 999 /* 適当に変更のこと */ #define OUT_FILE "data_list" /* 〃 */ #define MEM_SIZE 256 /* 読み込みサイズ */ #define PROCS 3 /* 全プロセス数 */ #define OUTPUT 1 /* 標準出力パス */ #define ON 1 #define OFF 0 #define STDERR 2 /* エラー出力パス */ struct set1 { /* xyz.c = xyz2.c を厳守 */ char buffer[MEM_SIZE]; /* data buffer */ signed char flag[PROCS]; /* condtion flag */ pid_t id_number[PROCS]; /* id code */ } *ptr; int shmid; /* 共有メモリ関連 */ char *shmaddr; /* 〃 */ void no_ope(int); int main(void) { int nread; char command_line[128]; if (((shmid = shmget(CODE, MEM_SIZE + sizeof(*ptr), IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) || ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1)) exit(0); ptr = (struct set1 *)shmaddr; ptr->id_number[0] = getpid(); /* ↓ 手抜きです m(_ _)m */ sprintf(command_line, "./xyz2 %d >>%s &", CODE, OUT_FILE); if (system(command_line) != 0) exit(0); signal(SIGCONT, no_ope); /* シグナルハンドラー登録 */ while (1) { write(STDERR, "> ", 2); if ((nread = read(0, ptr->buffer, MEM_SIZE)) == -1) exit(0); if (ptr->buffer[0] == '!') { ptr->flag[OUTPUT] = ON; kill(ptr->id_number[OUTPUT], SIGCONT); break; } else { ptr->flag[OUTPUT] = OFF; ptr->buffer[nread - 1] = '\n'; ptr->buffer[nread] = '\0'; kill(ptr->id_number[OUTPUT], SIGCONT); // ←に、出力プログラムを書く pause(); // 子プロセスの出力完了まで待機 } } if (shmdt(shmaddr) == -1) exit(0); return 0; } void no_ope(int dummy) /* 何もしないシグナル処理関数 */ { ; } *-------------- ここまで ------------ /* xyz2.c parallel processing test program by gcc on Mac OSX * execution: 並列出力専用ですから、シェルプロンプトから起動しないこと。 * compile: gcc xyz2.c -o xyz2 */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> #include <signal.h> #define MEM_SIZE 256 /* xyz.c に合わせる */ #define PROCS 3 #define CODE 999 #define MY_ID 1 #define ON 1 #define OFF 0 #define PARENT 0 struct set1 { char buffer[MEM_SIZE]; /* data buffer */ signed char flag[PROCS]; /* condtion flag */ pid_t id_number[PROCS]; /* id code */ } *ptr; int shmid; char *shmaddr; void no_ope(int); int main(int argc, char *argv[]) { int temp_code; /* 親プロセスから受け継ぐことにしました */ temp_code = atoi(argv[--argc]); if (((shmid = shmget(temp_code, MEM_SIZE + sizeof(*ptr), IPC_CREAT | S_IRUSR | S_IWUSR)) == -1) || ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1)) exit(0); /* 前処理 */ ptr = (struct set1 *)shmaddr; ptr->id_number[MY_ID] = getpid(); ptr->flag[MY_ID] = OFF; signal(SIGCONT, no_ope); while (1) { pause(); // 親プロセスからのシグナルを待つ if (ptr->flag[MY_ID] != OFF) { kill(ptr->id_number[PARENT], SIGCONT); break; } else { printf("%s", ptr->buffer); kill(ptr->id_number[PARENT], SIGCONT); } } if (shmdt(shmaddr) == -1) exit(0); return 0; } void no_ope(int dummy) { ; }
- 和泉 博(@hiroshi09s)
- ベストアンサー率54% (59/109)
「2つのプロセスが同時に同じ共有メモリにアクセスすることは可能ですか?」との質問ですから、今話題のマルチコアあるいはメニーコアで行われるべき task parallelism(タスク並列)の問題ですよね。 結論からいえば可能です。ただし、これは米国特許事項であり、日本ではスパコンの話題が示すように認めたくない人が多いようです。それは the virtual programming language F によれば以下のように記述されます。書式はほぼCと同じですから、内容は理解できると思います。 プログラムはメインプログラムを主体に起動しますが、デーモン風にHDDに共有メモリの内容を書き出す send_hdd と terminal に出力する print_out プログラム、および万一に備え dead_lock 監視のフォールトトレラント機能を実現する dead_lock プログラムを並列起動して起きます。 各プログラムは、起動したならば自身のプロセスId を $buffer に示された struct 構造体で示される所定欄に書き込みます。その他、処理に必要な情報を書き込み、プログラム本体の処理に移ります。 1~3のデーモンプログラムは、補助的プログラムであるため通常は sleep するようにしますが、1の dead_lock はプロセス状態の監視用であるため、2~4の稼働状況をチェックしては 0.1~1秒間隔程度の sleep を繰り返します。 メインプログラムにおいて、shared memory の出力があるとき、signal を1~3のプロセスに送り、プロセスを wakeup させて同時に実行させることが出来ます。ただし、shared memory への書き込みに際しては2,3の動きに制約を設けなければなりません(セマフォなど)。 $buffer[] に格納される struct 構造体は、例えば my_job.h ヘッダーファイルとして定義しておき、各プログラムから #include “my_job.h” と宣言することで異プログラムながら同一プログラム感覚でプログラミングできるのです。 これら各プログラムの作成は、今使っているC言語を使い、更なる言語習得は不要です。このプログラム例から、下のプログラムは task parallelism を実現したものであるのがわかっていただけると思います。 これを使うことの利点は、shared memory をどうしようかと悩むことなく、Cプログラミングに集中できることです。 /* Sample program by the virtual programming language F. * file name: parallel_computing * execution: func "parallel_computing()" -m=17G */ shared /* Set global variable of shared memory */ $buffer[16G]; /* Keep buffer size 16G bytes */ %main($void) { dead_lock $buffer & // 1. dead lock を監視、制御する send_hdd $buffer & // 2. HDDに共有メモリを書き出す print_out $buffer & // 3. terminal に出力する main_prgm $buffer // 4. メイン・プログラム /* return は不要である。*/ }