No.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はプロセススケジューラが
ガタガタなので、まともに動作しないでしょう。
工夫が必要です。
これまでの質問に対する回答大変ありがとうございました。
とても的確で分かりやすい回答でした。
また、何か分からないことがあったら、質問に答えてください。
cloneでゾンビプロセスを回避する方法が分かりました。
__clone(xx, stk+4096, CLONE_VM|CLONE_SIGHAND, (void *)arg);
を
__clone(xx, stk+4096, CLONE_VM|CLONE_SIGHAND|SIGCHLD, (void *)arg);
にすると、クローンのプロセスが終了した時にSIGCHLDシグナルが親プロセスに送信されゾンビプロセスが出来ませんでした。
No.2
- 回答日時:
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で作ったプロセス間ではそれが伝わる、という言い方もできるかも
しれません。
分かりやすい説明ありがとうございます。
度々申し訳ありませんが、質問よろしいでしょうか。
今考えているシステムでは、プロセスの数が数千以上が同時に存在する場合があり、各プロセスに対して共有のメモリをshmXXで使用すると、必要なメモリが大量になる為にスレッドでのメモリの共有を考えています。
各プロセスに対して共有のメモリをshmXXで使用した場合、プロセス数*共有メモリのサイズ必要になると思うのですが、間違いはないでしょうか?
cloneで生成されたプロセスが、終了した時のゾンビプロセスの回避方法はどうすればよいのでしょうか?
No.1
- 回答日時:
質問のタイトルからすると、マルチスレッドしたいのでしょうか・・・。
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というシステムコールがあります。
ありがとうございました。大変参考になりました。
もう一つ質問ですが、cloneの使い方で呼ぶ関数は、内部関数のみなのですか?
実行ファイル等呼べるのであれば、使い方を教えて下さい。
cloneを使った場合にもpthread_mutex_lock等は使用可能ですか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- iPhone(アイフォーン) iCloudのデータをiPhoneメモリに移動したい 1 2022/10/24 22:11
- その他(学校・勉強) この中で間違ってある説明はありますか?詳しい方に教えていただきたいです。 A. 1つのプログラムが複 2 2023/07/14 01:15
- CPU・メモリ・マザーボード AUSU PRIME X570-PROというマザーボードを使用しており、タルコフというゲームを快適に 2 2022/12/31 15:38
- クレジットカード --引用-- プリペイドカードで共有口座を作ってるかたに質問です。 家族間で共有口座を作りたいのです 2 2023/05/06 12:19
- Windows 10 実装メモリとタスクマネージャーのメモリ使用量不一致について 4 2022/12/15 01:07
- CPU・メモリ・マザーボード AG03mk2使用時にデバイスが認識されなくなる【B550 phantom gaming 4】 2 2023/02/11 05:21
- Excel(エクセル) エクセル終了が遅くなった 7 2022/04/19 12:11
- Wi-Fi・無線LAN イーサネットテザリングと WiFiルーターの接続 7 2022/06/18 16:32
- FC2ブログ FC2ブログで文字を小さくしたいです。小さくする方法を教えてください。 1 2023/08/26 00:46
- FC2ブログ FC2ブログでプロフィールが2つ表示されます。 2 2023/08/14 17:05
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
バックグラウンドのプロセスの...
-
explorer.exeが異様にメモリを食う
-
Process.Startの戻り値を後で取得
-
sleep関数の精度について
-
VB6.0 SHELLで起動...
-
タスクマネージャーのプロセス...
-
共有メモリの同時アクセスにつ...
-
プロセスのアタッチ・デタッチ...
-
C#でのbatファイル実行結果取得
-
C++のプログラムをバックグラウ...
-
ShellExecuteが起動したプロセ...
-
プロセスIDからウィンドウハ...
-
バッチファイルでPINGログ取得
-
プロセスIDの取得方法について
-
別のプロセスの関数を呼び出す...
-
Windowsのユーザログイン/ログ...
-
怪しいプロセス教えてください。
-
ウィンドウのタイトルからプロ...
-
win10でpythonのプロセス名を変...
-
プロセスが実行中かどうか調べ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
バックグラウンドのプロセスの...
-
explorer.exeが異様にメモリを食う
-
なぜ女性は男性が忘れたことを...
-
タスクマネージャーのプロセス...
-
プロセスのアタッチ・デタッチ...
-
非表示になったエクセルは?
-
Process.Startの戻り値を後で取得
-
C#でのbatファイル実行結果取得
-
プロセスIDからウィンドウハ...
-
c言語でプロセスIDを調べたい
-
Linuxでのスレッド間メッセージ...
-
プロセスIDの取得方法について
-
ADOでアクセスのレコードに...
-
怪しいプロセス教えてください。
-
vb.netでEXCEL起動がうまくでき...
-
別のプロセスの関数を呼び出す...
-
C++のプログラムをバックグラウ...
-
Visual C++からpingを実行して...
-
VBS(WSH)で開いたIEのウィンド...
-
IISがフリーズ
おすすめ情報