fork()の挙動がいまいち良く分からないので質問させてください。
自分はPHPでなのですが、fork()はCで使われるのが多いだろうということと、概念を知りたいとの事でこちらで質問させていただきました。
以下のように書きLinuxの端末にて実行しました。
目的は、
1. 2つの子プロセスを作り、それらを同時並行処理したい
2. 同時並行処理なので3秒後に処理を終わらせて出力したい
という事です。
#!/usr/local/bin/php -q
<?php
$time = time();
$pid = pcntl_fork();
if ($pid == 0) {
$j;
for ($j=0; $j < 3; $j++) {
printf("child1: %d\n", $j);
sleep(1);
}
} else if($pid > 0) {
pcntl_wait($status);
print ("Parent-a\n");
} else {
die('fork できません');
}
$pid = pcntl_fork();
if ($pid == 0) {
$i;
for ($i=0; $i < 3; $i++) {
printf("child2: %d\n", $i);
sleep(1);
}
} else if($pid > 0) {
pcntl_wait($status);
print ("Parent-b\n");
} else {
die('fork できません');
}
echo "time:" . (time() - $time) . "sec\n";
すると、6秒後にまず
child1: 0
child1: 1
child1: 2
child2: 0
child2: 1
child2: 2
time:6sec
child1: 0
child1: 1
child1: 2
Parent-b
time:6sec
が出力され、その3秒後に
Parent-a
child2: 0
child2: 1
child2: 2
time:9sec
Parent-a
Parent-b
time:9sec
が出力されました。
分からない点は以下の通りです。
1. Parent-bがParent-aより前に表示されたり、
child1: 0 child1: 1 child1: 2 child2: 0 child2: 1 child2: 2
及びParent-a Parent-bを出力したいのに、
なんかそれ以外のものが色々と不思議な順序で出力されている上、
9秒も処理時間にかかり、並行処理ではなく逐次処理になっているように見える。
2. 確か"Unix/Linuxプログラミング理論と実践"だったと思うのですが、Unix系の本にて
子作成(親のコピー) -> 子処理中、親は居眠り -> 子exit() -> 親wait() -> 親起きる
という感じで書かれていたように思いますが、
実行例のParent-a等を見ると、挙動が分かりません。
長くなり申し訳ございませんが、もし宜しければ間違っている点をご指摘していただけないでしょうか?
また、上記に書いた"目的"を実現するためのCなどで宜しいですのでコード例など部分だけで宜しいですので挙げて頂けたら幸いです。
申し訳ございませんが、宜しくお願いいたします。
A 回答 (4件)
- 最新から表示
- 回答順に表示
No.4
- 回答日時:
他の方が回答されているように、
親子の両プロセスがそれぞれ2回目のfork()で枝分かれしている点、
それから1つ目の子プロセスを wait() してから、
2つ目の子プロセスを fork() している点が問題です。
子プロセスの処理を関数にして分離すると
わかりやすいかもしれません。
↓
--
function child_proc($no){
for( $i = 0; $i < 3; $i++ ){
printf( "child%d: %d\n", $no, $i ); sleep( 1 );
}
exit( 100 + $no );
}
$pid1 = pcntl_fork();
if( $pid1 < 0 ){ die( "fork 1: error." ); }
if( $pid1 == 0 ){ child_proc( 1 ); }
$pid2 = pcntl_fork();
if( $pid2 < 0 ){ die( "fork 2: error." ); }
if( $pid2 == 0 ){ child_proc( 2 ); }
pcntl_waitpid( $pid1, $s );
printf( "(exit1) %d\n", pcntl_wexitstatus( $s ) );
pcntl_waitpid( $pid2, $s );
printf( "(exit2) %d\n", pcntl_wexitstatus( $s ) );
ありがとうございましたッ!
ちょっと変更しましたが、同様の記述をして、無事に自分のやりたかった事を実現できました。
本当にありがとうございました。
No.3
- 回答日時:
>親プロセスはchild2のように子プロセス処理が終わる度に、
>"pid>0"の処理を上から行う事になるのでしょうか?
child2の動作を誰もそういう風に説明してないですが・・・
前の説明よく見直してください。
説明としては#1のが詳しいですからそちらもよく読んでください。
>"Parent-a"が何故2回も出力されるのか分かりません。
バッファリングが関係しそうな気はしますがphpのバッファリング制御の事は
私わかりませんので、頑張って調べてください。
>また、時間も6秒かかっております。
child1が終了する(開始から3秒後)のを待ってから
child2をforkするように書かれてますので最低でも6秒かかるのは当然ですよ。
child1とchild2を並列動作させたいならCだとこんな感じ
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
pid_t child_pids[2];
for (int i = 0; i < sizeof(child_pids) / sizeof(child_pids[0]); i++) {
child_pids[i] = fork();
if ((int)child_pids[i] == -1) {
perror("fork()");
return 1;
}
else if (child_pids[i] == 0) {
for (int j = 0; j < 3; ++j) {
printf("child%d: %d\n", i, j);
sleep(1);
}
_exit(0);
}
else {
/* no-op */
}
}
while (1) {
pid_t pid;
int status;
int finished;
finished = 0;
pid = wait(&status);
for (int i = 0; i < sizeof(child_pids) / sizeof(child_pids[0]); i++) {
if (child_pids[i] == pid)
child_pids[i] = 0;
if (child_pids[i] == 0)
finished++;
}
if (finished == sizeof(child_pids) / sizeof(child_pids[0]))
break;
}
return 0;
}
>child2の動作を誰もそういう風に説明してないですが・・・
すみません、Parent-aが2度も出来てきたので、そうなのかなぁ、と思いまして書かせていただきました
コードの記述、ありがとうございました。
やはり、wait()の記述がミソだと言うことが分かりました。
本当にありがとうございました。
No.2
- 回答日時:
>2. 確か"Unix/Linuxプログラミング理論と実践"だったと思うのですが、Unix系の本にて
>子作成(親のコピー) -> 子処理中、親は居眠り -> 子exit() -> 親wait() -> 親起きる
>という感じで書かれていたように思いますが、
その書籍は読んだことありませんが、
「子処理中、親は居眠り」はおかしいですね。
「親wait()で子exit()まで居眠り」が正しいです。
>実行例のParent-a等を見ると、挙動が分かりません。
子exit()は明示的に書かないといけないですよ?
例に書かれているものはそれがないので
child1がchild2のfork()までしてます。
ご回答ありがとうございました。
確かにサンプルではexit()が抜けておりました。
ご指摘ありがとうございます。
$pid1 = pcntl_fork();
if ($pid1 == 0) {
$j;
for ($j=0; $j < 3; $j++) {
printf("child1: %d\n", $j);
sleep(1);
}
echo "現在のプロセスID=" . posix_getpid() . "\n";
exit();
} else if($pid1 > 0) {
pcntl_wait($status);
print ("Parent-a\n");
} else {
die('fork できません');
}
のように変えたら挙動が変わりました。
しかし、
child1: 0 child1: 1 child1: 2
現在のプロセスID=4504
Parent-a
child2: 0 child2: 1 child2: 2
現在のプロセスID=4505
Parent-a Parent-b
time:6sec
のようになります。
親プロセスはchild2のように子プロセス処理が終わる度に、
"pid>0"の処理を上から行う事になるのでしょうか?
"Parent-a"が何故2回も出力されるのか分かりません。
また、時間も6秒かかっております。
この場合、どの部分にchild2の処理(pid2=fork()以下)を書けば3秒で終わるようになるのでしょうか?
No.1
- 回答日時:
動きは多分こんなところ。
具体的なプロセスIDを表示するようにしたら、デバッグが楽になると思ます。親プロセス
|
【分岐】―子プロセス1
| |
【待ち】 |
: 【処理1】
: |
: 【分岐】―子プロセス2
: | |
: 【待ち】 |
: : |
: : 【処理2】
: : |
: 【再開】←【時間表示後終了】
: |
【再開】←【時間表示後終了】
|
【分岐】―子プロセス3
| |
【待ち】 |
: 【処理2】
: |
【再開】←【時間表示後終了】
|
【時間表示後終了】
非常に分かりやすい図を記述して頂きまして有難うございました。
視覚的に理解できました。
No.2でのwormholeさんのご指摘にあったようにexit();を加えたら変な挙動はおさまりました。
ただ、Parent-aが2回表示されているのですが、何故このような挙動になるのかは分かりません。
上記図のように、親プロセスは上から下へと向かうのではなく、wait()の際かなにかに戻るような事もあるのでしょうか?
それと、child1とchild2を同時並列処理をさせるには、親プロセスの
最初の【待ち】--->【再開】の中に入れこまなくてはいけないのではと思いますが、
fork()した、その下にもう一度fork()を2回打って・・・とか考えたのですが、頭が少し混乱してきました(苦笑)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【お題】絵本のタイトル
- ・【大喜利】世界最古のコンビニについて知ってる事を教えてください【投稿~10/10(木)】
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・ハマっている「お菓子」を教えて!
- ・最近、いつ泣きましたか?
- ・夏が終わったと感じる瞬間って、どんな時?
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
プロセス終了の検知について
-
怪しいプロセス教えてください。
-
指定した実行中プロセスの、指...
-
Windowsのユーザログイン/ログ...
-
ロードアベレージが高いのです...
-
異なるプロセス間でのイベント...
-
バックグラウンドのプロセスの...
-
プロセスIDからウィンドウハ...
-
タスクマネージャーのプロセス...
-
.NETでプロセスの一覧を取得
-
OSPFでプロセスを分ける意義に...
-
SendMessageが失敗するときがある
-
Windowsでのfork方法
-
WindowsAPIのCreateFile関数ま...
-
Macターミナルで実行中のプログ...
-
緯度、経度の 10進法と 60進法...
-
C# シリアル通信でデータ受信...
-
UWSCがうまく動かない
-
このレジの並び方は間違ってま...
-
マージャンのゲームを作りたい...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
バックグラウンドのプロセスの...
-
explorer.exeが異様にメモリを食う
-
プロセスのアタッチ・デタッチ...
-
C#でのbatファイル実行結果取得
-
タスクマネージャーのプロセス...
-
タスクマネージャーのプロセス...
-
プロセスIDの取得方法について
-
SendMessageが失敗するときがある
-
Process.Startの戻り値を後で取得
-
怪しいプロセス教えてください。
-
C++のプログラムをバックグラウ...
-
IISがフリーズ
-
VB.NET 自プログラムのプロセス...
-
ADOでアクセスのレコードに...
-
共有メモリの同時アクセスにつ...
-
Linuxでのスレッド間メッセージ...
-
VB6.0 SHELLで起動...
-
別のプロセスの関数を呼び出す...
-
非表示になったエクセルは?
-
Windowsでのfork方法
おすすめ情報