プロセス間で共有できるメモリの使い方を教えてください。
(システムコールshmget、shmat、shmdt以外で。)
使用例など有りましたら是非お願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

shmでメモリを共有した時に、プロセス数*共有メモリサイズを


使用するということはありません。

LinuxのOSとしてのメモリ管理の内部実装はよくは知りませんが、
今時のまっとうなUNIXであれば、共有メモリの実装は、
1つのメモリオブジェクトを複数のプロセスからポイントする、
というように実装されているかと思います。

普通の状態では、仮想アドレス空間から物理アドレス空間への
変換テーブルをプロセスごとユニークなものを持ちます。

メモリオブジェクトとはその変換テーブルを管理するOS内の資源の単位で、
おおざっぱに言えば、プロセスが使用するメモリ空間の、
テキスト領域(プログラムのコードを格納する領域)、
データ領域(グローバル変数を格納する領域)、スタック領域(ローカル
変数を格納する領域)、という単位でオブジェクトが作られます。
つまり、1プロセスにつき、この3種類の領域に対応する
3つのメモリオブジェクトへのポインタを持っていて、
それぞれのオブジェクトは変換テーブルを持ちます。

別プロセス間でメモリを共有するという状態は、
共有している領域へのメモリオブジェクトへのポインタが
同じメモリオブジェクトを指している状態であります。

ですから、メモリオブジェクトはまさに「共有/共用」されており、
プロセス数分メモリを消費するということはありません。
但し、わずかですが管理領域分はプロセス数分使うでしょう。。

あと、cloneで作成した子プロセスが親プロセスより先に終了すると
ご指摘のとおりdefunct(ゾンビ)ができます。
こいつを消すには親がwaitしてあげるしかなさそうです。。。
signal(SIGCHLD, SIG_IGN)をしてみましたが、ダメでしたね。。。

pthread_createで作られたスレッド(cloneプロセス)では、
pthreadライブラリの中で上手に解決しているらしく、
defunctはできませんでしたので、こちらを使うほうがお手軽のようです。

もう、ここまできたらついでなので、サンプルを紹介します。

-- clone ---
#include <sched.h>
#include <signal.h>

char stk[4096];

main()
{
int xx();
int arg[1];

signal(SIGCHLD, SIG_IGN);

arg[0] = 555;
__clone(xx, stk+4096, CLONE_VM|CLONE_SIGHAND, (void *)arg);
while (1);
}

int
xx(void *arg)
{
int x;

x = (int)((int *)arg)[0];
printf("x=%d\n", x);
}
-- clone ---

単にccして、実行できます。
親側はwhileで止まっていますので、ps -ef で見ると
defunctが残っているのがわかります

-- pthread --
#include <pthread.h>

int
main()
{
void *xx();
pthread_t pth;
int arg[1];

arg[0]=555;
pthread_create(&pth, NULL, xx, (void*)arg);
while (1);
}

void*
xx(void *arg)
{
int x;

x = (int)((int *)arg)[0];
printf("x=%d\n", x);
}
-- pthread --

ccするときに、-lpthread を指定してください。
同じように親は止まっていますが、今度はゾンビが
ないのがわかると思います。


-- shmget/shmat メモリ提供側 --
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#define DSIZE 1000000

int
main(int argc, char **argv)
{
key_t key = 123;
int shmid;
int *p;
int i;

if (argc > 1)
key = atoi(argv[1]);

if ((shmid = shmget(key, sizeof(int)*DSIZE,
IPC_CREAT|SHM_R|SHM_W)) == -1) {
fprintf(stderr, "Can't get shmid\n");
exit(1);
}
printf("shmid=%d\n", shmid);

if ((p = (int*) shmat(shmid, 0, SHM_RND)) == (int*)-1) {
fprintf(stderr, "Can't attach shmid\n");
exit(1);
}
printf("p=%08x\n", p);

for (i = 0; i < 100; i++) {
*p = i;
p++;
}

while (1);

return 0;
}
-- shmget/shmat メモリ提供側 --

-- shmget/shmat メモリ共有側 --
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#define DSIZE 1000000

int
main(int argc, char **argv)
{
key_t key = 123;
int shmid;
int *p;
int i;

if (argc > 1)
key = atoi(argv[1]);

if ((shmid = shmget(key, sizeof(int)*DSIZE,
SHM_R)) == -1) {
fprintf(stderr, "Can't get shmid\n");
exit(1);
}
printf("shmid=%d\n", shmid);

if ((p = (int*) shmat(shmid, 0, SHM_RND)) == (int*)-1) {
fprintf(stderr, "Can't attach shmid\n");
exit(1);
}
printf("p=%08x\n", p);

for (i = 0; i < 100; i++) {
printf("%d\n", *p);
p++;
}
shmdt(p);

return 0;
}
-- shmget/shmat メモリ共有側 --

単にccで実行できます。
端末を2つ開いて、提供側を実行しておいてから、
共有側を実行すると、値が共有されているのが
わかると思います。


数千のプロセスですか、ネットワーク対戦ゲームでも
作成されるのでしょうか? 楽しみですね。

ところで、数千のプロセスはたぶんシステムのプロセス数上限に
あたり実行できないと思います。
上限設定を変えて実行できたとしても、Linuxはプロセススケジューラが
ガタガタなので、まともに動作しないでしょう。

工夫が必要です。
    • good
    • 0
この回答へのお礼

これまでの質問に対する回答大変ありがとうございました。
とても的確で分かりやすい回答でした。
また、何か分からないことがあったら、質問に答えてください。


cloneでゾンビプロセスを回避する方法が分かりました。

__clone(xx, stk+4096, CLONE_VM|CLONE_SIGHAND, (void *)arg);


__clone(xx, stk+4096, CLONE_VM|CLONE_SIGHAND|SIGCHLD, (void *)arg);

にすると、クローンのプロセスが終了した時にSIGCHLDシグナルが親プロセスに送信されゾンビプロセスが出来ませんでした。

お礼日時:2001/02/16 11:16

Linuxの話で良いようですね。

。。

お近くにLinuxが使えるPCがあれば、その端末で
「man clone」にてcloneシステムコールの使い方などが
ごらんになれると思います。
それによると、残念ながらcloneで並列化できるのは、
関数のみで、実行ファイルは呼び出せません。

cloneのあとにexecシステムコールを呼び出すことで、
実行ファイルは呼び出せるでしょうが、もはやメモリの
共有はなくなります。

cloneやpthread_createにて作られるメモリの共有関係ではない
共有メモリをご使用になりたいのであれば、shmやmmapを
使うしかないと思います。
単にプロセス間通信をしたいのであれば、pipeや、socketという
手もあります。

何をされたいのかよくわかりませんが、別の実行ファイルを
呼び出した上で、それとの間で共有メモリ、、ということであれば、
スレッドの話は関係ないかと思います。
どうしてshmを使いたくないのかわかりませんが、、、
もしかして面倒なのですか?
shmgetや、mmapをmanでごらんになってみてください。

UNIXやLinuxでのプログラミングを解説している本を1冊
読んでみるのも良いかと思います。

cloneで作ったプロセス間で、pthread_mutex_lock等が有効に
機能するかどうかは知りません。。。
動くかもしれませんね。


ちなみに、おせっかいですが、題になっている「スレッドとは?」

cloneやpthread_createで作られるスレッド
(Linuxではライトウェイトプロセスと言ったほうが良いのかも
しれませんが)とは、CPUの割り当てられる実行の単位です。
Machオペレーティングシステムでは、スレッドと対になる
概念としてタスクがあり、タスクとはメモリ空間や、
ファイルディスクリプタなどのプログラムの実行の環境のことを
差します。

スレッドがタスクという資源を使って動作することにより、
CPUがメモリやファイルを参照しながら動作する、、というのと近い
イメージになります。
普通のUNIXでいうプロセスは、1タスクにつき1スレッドが対応している
ことになります。

「マルチスレッド」とは、1つのタスクの中に複数の
スレッドがある状態を指し、おのおののスレッドは、
タスクを共有するため、メモリを共有していることになります。

普通、マルチプロセスではなくマルチスレッドを使うのは、
プロセスよりも小さい単位で、処理を並列化したい場合に使用します。
これは、マルチCPUのコンピュータで大きな効果をあげるために
よく利用されます。
例えば、forループで10000回繰り返すような演算で、
1回ごとの処理は最終結果を出すところまで関連しない、、
なんて場合に、CPUが2個とか4個とかあれば、それぞれ5000回ずつとか、
2500回ずつ受け持つことで処理時間を短くできます。

普通、プロセスの切り替えよりもスレッドの切り替えのほうが
オーバーヘッドが小さいというのも、理由のひとつです。

Linuxのpthread_createは、その延長で結局はcloneシステムコールが
呼び出されます。
実は別のプロセスが作られていて(psコマンドでもそれは
確認できます)、1タスクに複数スレッド、、というイメージをは
違いますが、メモリを共有させることで、マルチスレッドと
同じような処理を行うことができるわけです。。。

さらに蛇足ですが、普通のUNIXではプロセスの生成には
forkシステムコールを使います。Linuxにもあります。
fork後の呼び出し元プロセスと生成されたプロセスの
メモリは参照においてのみ共有されています。。
ですが、お互いのメモリの書き換えは、相手には伝わりません。
cloneで作ったプロセス間ではそれが伝わる、という言い方もできるかも
しれません。
    • good
    • 0
この回答へのお礼

分かりやすい説明ありがとうございます。
度々申し訳ありませんが、質問よろしいでしょうか。

今考えているシステムでは、プロセスの数が数千以上が同時に存在する場合があり、各プロセスに対して共有のメモリをshmXXで使用すると、必要なメモリが大量になる為にスレッドでのメモリの共有を考えています。

各プロセスに対して共有のメモリをshmXXで使用した場合、プロセス数*共有メモリのサイズ必要になると思うのですが、間違いはないでしょうか?

cloneで生成されたプロセスが、終了した時のゾンビプロセスの回避方法はどうすればよいのでしょうか?

お礼日時:2001/02/15 11:55

質問のタイトルからすると、マルチスレッドしたいのでしょうか・・・。



Linuxと仮定しますと、、
(下記のソースは概要であり、このまま動作しません)

#include <pthread.h>

#define NTHREADS 10 /* 適当 */
#define NARGS 5 /* 適当 */

pthread_t thread[NTHREADS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int data = 5; /* 適当 */

xxx()
{
int i;
int arg[NARGS];
int yyy();

for (i = 0; i < NTHREADS; i++) {
arg[0] = i; /* 適当 */
pthread_create(&thread[i], NULL, yyy, (void*)arg);
}
}

yyy(void *arg)
{
int x;

x = ((int *)arg)[0];

pthread_mutex_lock(&mutex);
data++;
pthread_mutex_unlock(&mutex);
printf("%d) %d\n", x, data);
}

Linuxでは、スレッドの変わりにクローンプロセスというのができあがり、
見かけ上は、プロセス内に複数のスレッドができたのと同じように振舞わせるように、
pthreadライブラリがあり、それを上記のように使うと楽です。

スレッド(クローンプロセス)間では、スタック(ローカル変数)以外の、
データ(グローバル変数)は、共有されています。
(cloneシステムコールを使うと、もっと詳細に共有部分を設定できます)

共有しているデータへのアクセスの際に、
スレッド(クローンプロセス)間での同期をとるために、
pthread_mutex_lock/unlock を使用します。

上の例では、関数yyy()がマルチスレッド(クローンプロセス)により
実行されます。


ちなみに、
親子関係でないプロセス間で、本当の意味で共有メモリをするのであれば、
shmget/mat以外には、mmapというシステムコールがあります。
    • good
    • 0
この回答へのお礼

ありがとうございました。大変参考になりました。
もう一つ質問ですが、cloneの使い方で呼ぶ関数は、内部関数のみなのですか?
実行ファイル等呼べるのであれば、使い方を教えて下さい。
cloneを使った場合にもpthread_mutex_lock等は使用可能ですか?

お礼日時:2001/02/13 10:01

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

Qセマフォとmutexの違いは?

排他制御としてセマフォとmutexがありますが、
この二つの違いがよくわかりません。
自分で調べてみたところ、
・セマフォ…プロセス間排他制御。複数ロックがかけられる。
・mutex…スレッド間排他制御。ロックは一つだけ。

と言うような違いがあるようなのですが、これだけの差なんでしょうか?
(これだけの差、と言ってる時点で筋違いだったら申し訳ありません)
また、セマフォをスレッド間排他制御に用いたり、
mutexをプロセス間排他制御に用いることは可能なのでしょうか?
可能だとしたら、これらが2種類存在する理由も教えていただきたいです。

Aベストアンサー

> >一般論としては、Mutexは「カウントを1に限定した」特殊化したSemaphoreです。
> とのことですが、これはWin32以外の環境だと
> これ以上の違いはないと言うことでしょうか?

「一般論」と書いた意味を取り落とされているのではないかと思います。

「一般論としては〇〇」なのですから、「Win32以外の環境」も含めて「各論」では「必ずしも〇〇とは限らない」とご理解ください。

なお、ご指摘のとおり、あるリソースを排他的に利用するだけであればMutexを使用するかわりにカウントが1のSemaphoreを使用することができます。(特定の環境での、MutexとSemaphoreの環境依存の動作を除けば、ですが。)

ではなぜ2種類が用意されている(用意されている環境が存在する)のかといえば、リソースの排他的利用は非常によくあることなので、これに特化した機能を用意すればより良いであろう、というシステムデザイナの判断によるものと考えられます。

ここで言う「より良い」は、あるデザイナにとっては「便利性」、また別のデザイナにとっては「消費リソースが少ない」と、これまた考え方はいろいろでしょう。

> >一般論としては、Mutexは「カウントを1に限定した」特殊化したSemaphoreです。
> とのことですが、これはWin32以外の環境だと
> これ以上の違いはないと言うことでしょうか?

「一般論」と書いた意味を取り落とされているのではないかと思います。

「一般論としては〇〇」なのですから、「Win32以外の環境」も含めて「各論」では「必ずしも〇〇とは限らない」とご理解ください。

なお、ご指摘のとおり、あるリソースを排他的に利用するだけであればMutexを使用するかわりにカウントが1のSemaphore...続きを読む

QC++とVC++の違いについて

この質問をご覧頂きありがとうございます。

みなさんに2つ、お聞きしたいことがあり、質問させていただきます。
1.C++とVC++とでは、どのくらいの違い(どういう違い、特徴)があるのでしょうか?

2.また、これからC++/VC++でプログラミングを始めようとした場合、
C++からVC++へと段階を踏んで勉強した方がよいのでしょうか?

一応、私のプログラミングの履歴としては、
言語としてVisual Basicは多少勉強はしておりました。
また、オブジェクト指向の基礎程度(多相、継承、カプセル化)は理解しています。

抽象的な質問で大変恐縮なのですが、
みなさんの経験論や、ご参考にした本、HP等がありましたら、
ご教授いただければと思います。

Aベストアンサー

こんにちは、honiyonです。
 VC++ は、Microsoftの発売している C++開発環境です。 純正C++に加え、独自に機能(という言い方でいいのかな?)を追加しており、更に開発しやすいソフト(環境)を添付してあります。

 VC++に限らず、大体はそうです。 ProC++も、Borland C++も皆そうです。
 今までお使いになっていた VBも、BASICコンパイラ+開発環境です。 
 こちらの場合、BASICに機能を追加・・・というより仕様そのものを大幅いじってますね(笑)

 VC++をただ単純に C++として使用するなら良いですが、VC++とMFCを使用してウインドウアプリケーションを作りたいという事であれば、死に物狂いな勉強が必要が必要かもしれません。Windowアプリケーションの処理プロセスを理解していないと、とっても難解に感じると思います。
 VC++でなければいや!という理由がなく、Visual Basicからスムーズに移行でき、かつVisual Basicより奥の深い事をしたい、というならば C++ Builder をお勧めします。 こちらで腕を慣らし、徐々にWindowアプリケーションの処理プロセス(WinMainから始まるプログラムの組み方の事です)を学ぶと良いかな、と思います。 その上で VC++&MFCな環境に移行すると、敷居はぐっと低くなります。
 私は一度VC++&MFCに挫折しましたが、こちらの方法で何とか理解する事が出来ました(^^;

 参考になれば幸いです(..
 

こんにちは、honiyonです。
 VC++ は、Microsoftの発売している C++開発環境です。 純正C++に加え、独自に機能(という言い方でいいのかな?)を追加しており、更に開発しやすいソフト(環境)を添付してあります。

 VC++に限らず、大体はそうです。 ProC++も、Borland C++も皆そうです。
 今までお使いになっていた VBも、BASICコンパイラ+開発環境です。 
 こちらの場合、BASICに機能を追加・・・というより仕様そのものを大幅いじってますね(笑)

 VC++をただ単純に C++として使用するなら良いで...続きを読む

Q共有メモリについて

shmget関数のサイズ指定について質問があります。

共有メモリ上にキー番号「1234」で作られている状態で、
以下の関数(サイズ指定ありなし)を呼び出した際の違
いは何なのでしょうか?
単純にキー1234にアタッチしたいだけなら、サイズ0指定を
使用するのでしょうか?

shmget(1234,0,IPC_ALLOC)
shmget(1234,sizeof(int),IPC_ALLOC)

Aベストアンサー

IPC_ALLOC は、どうやら、単に 0 と定義されているマクロではなさそうですね。

http://www-igm.univ-mlv.fr/~dr/HEVEA/

なので、mode_t mode = SHM_R | SHM_R >> 3 | SHM_R >> 6; (適当に決めたアクセスモード)として、

 shmget(1234, 0, mode);

と、

 shmget(1234, sizeof(int), IPC_ALLOC | mode);

の意味は違うかもしれませんね^^ 上の方は単に存在すればよく、下の方は存在してかつ size をチェックし size が同じ(あるいはそれ以上)という意味になるのかもしれません。移植性を考慮するなら、IPC_ALLOC は使わないほうがいいと思いますけれど…^^

レスがあまりないので、表示通りに「困って」る風には思えないので、まあいいんでしょうけど(笑)参考までに^^

Qスレッドの安全な終了のさせ方

スレッドの安全な終了のさせ方

 メインスレッドにてCreateThread命令を使い、あるサブスレッドを作りました。
このサブスレッドは内部でmallocを使い動的に配列領域を確保して
その配列領域をforループ等で「かなり時間の掛かる処理」として繰り返し
アクセスしています。
ループが終了した時に「free」を実行してmalloc領域を開放しています。

アプリ終了時にメインスレッドからこのサブスレッドを終了させるのに
メインウインドウにWM_DESTROYメッセージが送られた時、これまで単に
そこで「CloseHandle(hSubThread);」とだけ書いていたのですが、
もしかしたらこれでは場合によっては(サブスレッドがループ処理中だったら)
malloc領域が開放されずにリークしてしまうのではないかと思いました。

 そこでイベントオブジェクトを使い、サブスレッドがループ処理中の
時には非シグナル状態にして、ループが終了しfreeで領域を開放した後
シグナル状態にするということにして、メインスレッドはそれを
WaitForSingleObjectで待つという構造にしました。

ところが「メインスレッドに待ちを作るな」という言葉通り、これでは
上手く行きませんでした。サブスレッドはその時間の掛かる処理の
最中でSendMassage等でメインスレッドの処理を促すような命令を
(例えばその処理の進捗状況を表示するなど)を幾つも行っていたので、
もしWaitFor~でメインを待たせると「サブスレッドの処理も進まなくなり
結果両方がロックして動かなくなってしまう」という悲しい状況に
嵌ってしまうのです。

SendMessageを徹底的に無くすということも考えたのですが、
(例えばPostMessageに書き換えるなどもやってみたのですが、これは
全く意図した動作をしてくれない場合もあり)、別の方法では
どうしても代替できないケースもあって、全て消すというのは
現実的ではないのかもと。。

 このようなサブスレッドを安全に終了させるにはどうしたら良いでしょうか?
あるいは単にデストロイ時にCloseHandleとするだけでも良いのでしょうか?

スレッドの安全な終了のさせ方

 メインスレッドにてCreateThread命令を使い、あるサブスレッドを作りました。
このサブスレッドは内部でmallocを使い動的に配列領域を確保して
その配列領域をforループ等で「かなり時間の掛かる処理」として繰り返し
アクセスしています。
ループが終了した時に「free」を実行してmalloc領域を開放しています。

アプリ終了時にメインスレッドからこのサブスレッドを終了させるのに
メインウインドウにWM_DESTROYメッセージが送られた時、これまで単に
そこで「CloseHandle(hSu...続きを読む

Aベストアンサー

>SetEvent(hEventObject1);//イベントオブジェクトをシグナルに
スレッド終了を判断する場合はスレッドのハンドル自身を見た方が確実です。
HANDLE thread_handle = ::CreateThread(略);
(略)
::WaitForSingleObject( thread_handle , INFINITE );
スレッドは終了時にハンドルがシグナル状態になります。


>SendMessage(hMainWnd,....);
>//メインウインドウに何かのメッセージを送信
>//なってた時に処理が進まなくなる。
名前から察するにhMainWndはメインスレッドで動いているようですが
そのメインスレッドの処理がWaitForSingleObjectによって止まっているのなら処理は返ってきません。
つまりサブスレッドがメインスレッドとなんらかのやりとりをしたいなら、
この時点でメイン側はWaitForSingleObjectで待ってはいけません。

1. Main -> Subに終了前準備しろと通知
2. Sub -> Mainに終了前準備完了を通知
3. Main -> Subに終了しろと通知
4. MainはSubが終了するのをWaitForSingleObjectで待つ。

>SetEvent(hEventObject1);//イベントオブジェクトをシグナルに
スレッド終了を判断する場合はスレッドのハンドル自身を見た方が確実です。
HANDLE thread_handle = ::CreateThread(略);
(略)
::WaitForSingleObject( thread_handle , INFINITE );
スレッドは終了時にハンドルがシグナル状態になります。


>SendMessage(hMainWnd,....);
>//メインウインドウに何かのメッセージを送信
>//なってた時に処理が進まなくなる。
名前から察するにhMainWndはメインスレッドで動いているようですが
そのメインスレッドの...続きを読む


人気Q&Aランキング