数百ものプログラムを同時に実行するには?並行処理の方法

このQ&Aのポイント
  • 数百ものプログラムを同時に実行する方法について考えています。初期のGoogleが1台のコンピュータで300ものクローラーを同時に動かしていたことを思い出しましたが、実装方法がわかりません。forkを使えば可能かもしれませんが、処理が全て終わるまで待たなければならず、エラーの挙動も扱いにくいです。一方、一つのプログラムを書いてcronで一気に実行する方法も考えましたが、連続的な挙動ができません。並行処理を実行する方法と、クローラーに関する情報についてアドバイスを頂きたいです。
  • 数百ものプログラムを同時に実行する方法についてアドバイスを頂きたいです。Googleの初期のクローラーが1台のコンピュータで300ものクローラーを同時に動かしていましたが、自分の実装方法が思いつきません。forkを使えば可能かもしれませんが、処理が全て終わるまで待たなければならず、エラーの挙動も難しいです。一つのプログラムを書いてcronで一気に実行する方法も考えましたが、連続的な挙動ができません。並行処理の方法とクローラーに関する情報について教えてください。
  • 数百ものプログラムを同時に実行する方法について教えてください。初期のGoogleでは1台のコンピュータで300ものクローラーを同時に動作させていたそうですが、自分は実装方法が分かりません。forkを使えば実行可能かもしれませんが、処理が全て終わるまで待たないといけず、エラーの処理も複雑です。一つのプログラムを書いてcronで一気に実行する方法も考えましたが、連続的な動作ができません。並行処理の方法とクローラーに関する情報を教えてください。
回答を見る
  • ベストアンサー

数百ものプログラムを同時に実行するには?

並行処理の事を考えています。 初期のGoogleは1台のコンピュータで300ものクローラーを同時に動かしていたというようなことをある記事で読んだ記憶があります。 その事を思い出して考えてみたのですが、実装の方法が思いつきません。 例えばCなどのプログラミングでも、PHPなどのスクリプトでも、forkを使用すれば(for文などの中にクローラ用の関数入れてopenMPなどでも出来るかも)可能かもしれませんが、それだと処理が全て終わるまで待たないといけないのと、エラーがあった時の挙動は300も子プロセス使ってると実装が大変じゃないかなと思いました。 逆に、一つプログラムを書いて、cronにて大量に一気に実行する方が簡単じゃないかなと思いました。 でもそれでは終了時に止まって連続的な挙動はしないでしょう。 このような並行処理はどのようにして実行すればいいのでしょうか? forkを使うとしても、いったいどのタイミングで呼び出せばいいのか思いつきません。 アドバイス頂けたらと思います。 クローラーに関する情報が自分の調べ方では出てこなかったのですが、何かいい資料などありましたら、そちらも教えていただけたらありがたいです。 英文サイトなどでも大丈夫です。

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8526/19383)
回答No.6

>ただ、自分がお聞きしたかったことは、起動プログラムの事でした。 起動プログラムは以下のようになるでしょう。 pid_t pid[300],rpid; int status; for (int i = 0;i < 300;i++) {  pid[i] = fork();  if (pid[i] < 0) {   //fork error   continue;  } else if (pid == 0) {   //自分は子プロセス     //クローラーを起動。execl(),execv()などを使う   exec**(~~~);     //exec error   exit(-1);  } } //子プロセスが常に300個になるように保つ処理 for(;;) {  //子プロセスのどれかが終了するまで待つ  rpid = waitpid(-1,&status,0);  //以降、rpidのプロセスの終了状態を調べ、後始末する  //どのプロセスが終了したか、pid配列とrpidを比較  for (int i = 0;i < 300;i++)  {   if (rpid == pid[i])   {    //pid[i]が終了してしまったプロセスなので、再起動    pid[i] = fork();    if (pid[i] < 0) {     //fork error     continue;    } else if (pid == 0) {     //自分は子プロセス       //クローラーを起動。execl(),execv()などを使う     exec**(~~~);       //exec error     exit(-1);    }   }  } } なお、上記サンプルはデバッグもテストもしてない(というか、コンパイルさえしてない)ので、正常に動く保証はありません。手抜きで無限ループして終了する方法も無いですし。 なので「こういう風に書く」と言うサンプルとして参考にして下さい。 あと、起動プログラムとクローラープログラムは「別の実行ファイルにして、別々に作成」しましょう。 こうすると、クローラーは「致命的エラーが無い限り、クロールだけをし続け、致命的エラーがあったら後始末をしてから終了する」という作り方をすれば良いので、その方が簡単です。

chopperin
質問者

お礼

サンプルのソースまであげて頂きまして、本当にありがとうございました。 やはりコードは分かりやすいです。 概要が見えました。 ありがとうございました。

その他の回答 (5)

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.5

>つまり、起動させるプログラムは一つと言うことですよね。 そういうことです。 >で、その子プロセスのpidを管理し、エラーなどまで監視する。 「エラーなどまで監視する」というわけではなく waitpidなどのwait系システムコールはステータスが変化したプロセスのpidと、 どういうステータス変化が起きたのかがわかりますから、 それで判断するだけです。

chopperin
質問者

お礼

何度もお答えいただきまして、本当にありがとうございます。 ここの質問欄でだいたい自分の考えるべき筋道が見えてきました。 ありがとうございました。

  • chie65535
  • ベストアンサー率43% (8526/19383)
回答No.4

追記と訂正。 >すると >A-1:○年○月○日に解析して、今は誰も何もしてない。 >A-2:誰かがクロール中で、解析中になっている。 >のどちらかの答えがデータベースから返ります。 A-3:そのURLはデータベースに無い。 もありました。A-3の場合は、データベースに「今、自分が解析中」とURLを登録してから、解析に入ります。

  • chie65535
  • ベストアンサー率43% (8526/19383)
回答No.3

平行処理しても、単独処理しても「結果は1つ」です。 クローラーに限った話ではないですが、何をしたとしても「結果を1つ残す」のは間違いありません。 それら「結果」を格納する場所は、通常、排他制御されたデータベースを使います。 クローラーの誰か一人が「とあるURL」を調べようと思いました。 その場合、データベースに問い合わせて「このURLは、今、どうなってる?」と聞きます。 すると A-1:○年○月○日に解析して、今は誰も何もしてない。 A-2:誰かがクロール中で、解析中になっている。 のどちらかの答えがデータベースから返ります。 A-1の答えだった場合は「今から俺がクロールする」とデータベースを更新して、解析し、解析が終わったら「今日、解析した」とデータベースを再更新します。 この時、もし「データベース内のページ最終更新日」と「実際に見に行ったページの更新日」が同じだったら、実際の解析はしません。 解析中に「データベースに無いURLのリンク先」を見付けたら、それをデータベースに登録し「まだ解析してない」と言う状態にします。 A-2の答えだった場合は、何もせず、解析を終わります。 解析が終わって暇になったクローラーは、データベースに「まだ解析してない物が無いか?」と問い合わせて、その中から、最も古くにデータベースに登録した物を選び、次の解析に入ります。 もし、データベースに問い合わせた結果「解析してない物が無い」と言う場合は、データベースにある中から「最後に解析したのが最も古いもの」を選び、、次の解析に入ります。 クローラーを起動して管理するプログラムは「規定数に達するまで、ただ単にクローラーを起動するだけ」と「異常終了したクローラーが居たら、そいつの後始末」だけをします。 クローラーは「結果の集大成」であるデータベースを見ながら、そのデータベースを更新する」と言う作業を、延々と繰り返すだけです。 どんな並列処理プログラムであれ、必ず「たった一つの結果を更新する」に過ぎないので、その「結果の集大成」さえちゃんと「排他処理」しておけば、特に難しい事はありません。

chopperin
質問者

お礼

詳細なご説明、ありがとうございました。 非常に勉強になりました。 ただ、自分がお聞きしたかったことは、起動プログラムの事でした。 数百ものプログラムを一気に動かす為には、一つのメインのプログラムファイル(例えばc言語で書かれたmain.c)を最初に実行して、その中からfork()により(例えば)crawl.cまたはfunction crawl()を子プロセスとして呼び出し実行するのか、 それとも、(1台のパソコンだと仮定して)crawl.cと同時にインデックスを作成するindex.cを一気に沢山実行する方がいいのかと言う事でした。 この場合、crawl.cは止まらない挙動になると思います。 あるサイトを見て解析後urlを確認、DBなどからurl訪問のフラグを確認し、未訪問ならそれらのページヘ自ら訪問、または子プロセスを作成し、自己関数呼び出しとか。 上の方のやり方の方がいいとは思うのですが。

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.2

自分の考えていることはクローラではないのですが、同じような原理なのでクローラの話でいきますが、 しかし、クローラが終了後に次のurlへ行くのはどのようにしているのでしょうか? >urlの判断後、同じプロセスが、または子プロセスが訪問しているのではないだろうかと思っていたのですが。 >それとも、1ページを判断、分析後、そのプロセスは終了し、分析して見つけたurlを訪問リストに送り、それを別のプロセスが同じように処理していくと言うことでしょうか? 分析して見つけたurlを記録しておいて、順次自プロセスで辿っていくとかではダメなんですか? 見つけたurlの保存と次に行くべきurlを管理するプロセスを用意しておいて、見つけたurlを記録して貰い次に行くべきurlを教えて貰うとかでもいいでしょうし。 >自分の中ではまとめ役はforkして子プロセスの管理というのしか思い浮かばないのですが。 >最初から300のプロセスを産みだし、正常終了確認後新たなプロセスをfork、エラー発生時はログったりエラー処理をしてexit、そして新たなfork。 エラー発生時のログやエラー処理は、クローラーに任せてはいけないのでしょうか? まとめ役のプロセスはクローラーを必要な数だけ起動して異常終了したものがあれば起動し直す。 それくらいでいいと思うんですけど。 waitpid()とか使ったことありませんか?

chopperin
質問者

お礼

ご回答ありがとうございます。 つまり、起動させるプログラムは一つと言うことですよね。 そのプログラムが起動中にクローラなど何か処理の実行を行うプログラムを子プロセスとしてfork()により同時実行させると。 で、その子プロセスのpidを管理し、エラーなどまで監視する。 waitpid()は使ったことはあります。 それとも、管理プログラムは置かず、数百ものプログラムを一気に別々に起動させ、(ここではスケーラリングなど分散していないと仮定して)検索エンジンであればインデックス処理などの実行プログラムも同時に別々に動かす...という事ではないですよね?

  • wormhole
  • ベストアンサー率28% (1622/5659)
回答No.1

それぞれのクローラーを独立したプロセスで動かし、 それに加えてまとめ役のプロセスが1つあればいいだけだと思いますが。

chopperin
質問者

補足

ご回答ありがとうございます。 独立したプロセスですか。 自分の考えていることはクローラではないのですが、同じような原理なのでクローラの話でいきますが、 しかし、クローラが終了後に次のurlへ行くのはどのようにしているのでしょうか? urlの判断後、同じプロセスが、または子プロセスが訪問しているのではないだろうかと思っていたのですが。 それとも、1ページを判断、分析後、そのプロセスは終了し、分析して見つけたurlを訪問リストに送り、それを別のプロセスが同じように処理していくと言うことでしょうか? まとめ役のプロセスですか... 今自分にはイメージが出来ないのですが、そのプロセスはどのような挙動をするものなのでしょうか? 自分の中ではまとめ役はforkして子プロセスの管理というのしか思い浮かばないのですが。 最初から300のプロセスを産みだし、正常終了確認後新たなプロセスをfork、エラー発生時はログったりエラー処理をしてexit、そして新たなfork。 う~ん....

関連するQ&A

  • Cronで同じ処理を複数同時に実行するには?

    PHPで作ったWebサービスがあります。 ユーザーが100人いたとします。 この100ユーザーそれぞれのデータを、毎日0時(例えば)になると処理をするというCronを作りたいです。 PHPファイルは1ユーザー分の処理をするので、Cronで毎日0時に指定すると1人分の処理しかできません。 ではユーザー毎にCronを100個作るのは得策ではないと思います。 「一つのCron実行で100人分処理すれば?」というご意見もあるかもしれませんが、 1人分の処理が3~30秒かかるので、レンタルサーバーの1実行当たりの動作時間を超えてしまう問題があります。 毎日0時に100人分の処理を実行するのは無理なのでしょうか? そもそも共用のレンタルサーバーレベルでは、100人分の同時処理というのは現実的ではないでしょうか? そうすると、1人ずつ処理していくと、30秒×100人=50分の処理時間が必要になります。 こういう場合はどのように設計すれば良いのでしょうか? どうぞよろしくお願い致します。

    • ベストアンサー
    • PHP
  • csh スクリプトの実行者を知りたい

    スクリプト内部で、そのスクリプトを実行しているのが、 ユーザーなのか、cronなのかを判定することは可能でしょうか? 具体的には、hoge.cshというスクリプトがあり、 これはユーザーmogeのcronで週一回実行するよう登録されています。 さらに、このスクリプトは、メンテナンス時などに、 ユーザーmogeがコマンドラインからも実行することがあります。 スクリプト内部で、cronから走った時と、コマンドラインから 走らせた時で異なるlogを吐かせたいのですが、 どのようにしたらよいでしょうか。 宜しくお願いします。

  • Cygwin で Ftpを実行するTOOLを cronで実行したい。

    よろしくお願いします。 Cygwinで FTPを使ってUnixのFileを取得するシェルスクリプトを作成しました。 シェルの種類はkshです。 このTOOLをCygwinのcronに仕掛けて自動的にFileを取得するようにしたいのですが、 どうもFTPのところで止まってしまうようです。 Cron設置時にLogを取得したところ 下記のメッセージが出力されていました。   User (10.200.xx.xx(none)) 自分のhomeに.netrc ファイルを作成して、FTP時にはUserID/Passwordを入力しなくて済むようにしているので スクリプトを自分で実行する分には何も入力しなくて言いのですが cron実行時にはなぜかUserIDの入力を促すところでストップし、プロセスも残ってしまっています。 自動的にUserID/Pawwwordを読み込ませるにはどうしたらよろしいでしょうか?

  • Windowsで重い処理を実行したい

    CGIを作成している環境が Windows2000+IIS+ActivePerlで、時間がかかる処理を 行うプログラムを作成しています。 いろいろ調べて、alarm()とシグナルでブラウザタイムアウトを 防ぐ方法や、forkして子プロセスする方法などを、 試したのですが、うまくいかず、 よくよく調べてみるとWindows環境だと、alarmもforkも未実装らしく困り果てています。 IISでタイムアウト時間を延ばすのは避けたいです。 ブラウザのタイムアウトを回避しつつ、処理の長い CGIを実行するいい方法はないでしょうか?

    • ベストアンサー
    • Perl
  • 2つのプロセスを実行するCGI

    1つのCGIで2つのプロセスを実行することを考えています。 1つは、数分かかるような処理を行い、もう1つはユーザにその処理が”実行中”であるといったメッセージを表示しようと思っています。 CGIはC&C++で作成しようと考えており、forkして子プロセスで、数分かかる処理を実行し、親プロセスでメッセージ画面表示といった流れを考えています。 で、質問ですが、この場合、メッセージ画面で子プロセスが実行している間、砂時計が表示され続けますが、これを消す方法がありますでしょうか? また、1つのCGIで、このように2つのプロセスを実行する場合に、何か良い方法はありますでしょうか?

    • 締切済み
    • CGI
  • Cron で、CGIスクリプトを24時に実行したいのですが?

    Cron で、CGIスクリプトを、毎日、24時に実行したいのですが? どうやって、設定すればいいですか?

  • cronの挙動について

    cronの挙動について教えてください。 現在user「AAA」のcronにシェルスクリプトを登録しています。 コンソールから登録してあるスクリプトが正常に起動できることは確認できております。 しかしcron経由でコマンドが実行できない状態(/var/log/cronにコマンドを実行した履歴は残っています)でした。 色々試したところ、スクリプトの先頭で.bashrc(user「AAA」のもの)をsourceコマンドで実行するようにしたところ、cronからも起動できるようになりました。 (.bashrcには、スクリプト実行に必要な環境変数やパスの設定がされています) そこで質問なのですが、 (1) cronでコマンドを実行する場合、.bashrcは明示的に実行しなければいけないのでしょうか。 cronで実行される場合、ログインした状態と同じ環境で実行されるとばかり思っていたため、見落としていたのですが・・・。 以上、よろしくお願いいたします。

  • UNIX : cronにて自動実行すると、whoami が実行できない

    Kornシェルスクリプト(仮名:a.ksh)内の処理で、 「 whoami >> file1 」 をcron設定にて自動実行したところ、上記のコマンド部分が実行されておらず、file1(ファイル)にユーザ名が記載されていませんでした。 しかし、a.kshを手動実行しfile1を見ると、ユーザ名が記載されていて、上記のコマンド部分が実行できていることを確認致しました。 なぜcronにて自動実行すると、上記のコマンド部分が実行できないのでしょうか? 皆様、よろしくお願い致します。

  • 定期的に自動実行したい

    ある処理を30分おきに自動実行したいのですが、AM6時~AM8時の時間だけは実行したくない場合、CRONにはどのように登録すればよいでしょうか?

  • 同時実行数を指定&仕事は共有したい

    同時実行数を指定&仕事は共有したい 処理したいスクリプトが複数あります。 ・job_a.sh ・job_b.sh ・job_c.sh ・job_d.sh ・job_e.sh これらのスクリプトは同じリソースを利用するため、同時実行数は2つに制限したいです。 そこで下記のコマンド実行を思いつきました。コマンドの処理時間は事前にはわからないため、下記では偏りが発生し、実行を開始していないスクリプトが残っていても、1つの実行主体が終えてしまう場合があります。 > job_a.sh && job_b.sh && job_c.sh & > job_d.sh && job_e.sh & ココを、実行主体は終えずに残りのスクリプトを開始する方法はないでしょうか。 コマンドや、簡単なシェルスクリプトで実現できれば最高です。 こんな要望は、プログラムを作成しプロセスやスレッドを使って実現するべきでしょうか。 私はUbuntuを使っています。 ご助言をお願いします。