C言語 複数の子プロセスをwaitシステムコールで待つ方法
スポンサーリンク
このドキュメントの内容は、以下の通りです。
はじめに
パソコンでは、オペレーティングシステムが動作し、オペレーティングシステムの中でさまざまなソフトウェアが実行されます。FreeBSDやLinuxといったUnixオペレーティングシステムでは、実行中のプログラムをプロセスと呼びます。プロセスはオペレーティングシステムによって管理されます。プログラムから別のプログラムを実行することもできます。
たとえば、Unix で使われているシェルにコマンドを入力すると、シェルは新しいプロセスを作成して、コマンドを実行します。
ここでは、FreeBSDなどのUnix(やLinux)にて、C言語で子プロセスの処理をする方法を説明します。
プロセスの作成と終了
Unix システムでは、新しくコマンド・プログラムを実行するときに、プロセスを作成します。プロセスを作成する場合には fork (ふぉーく) システムコールを利用します。fork システムコールを呼び出すと、プログラムがあたかも2つに分離された状態になり、片方が親プロセス、もう片方が子プロセスとなります。Unixのシェルがコマンドを実行する場合には、まず、この fork を行います。そして、子プロセスで exec 系のシステムコールを呼び出し、外部プログラムを実行します。子プロセスは fork したときは、fork 前のプログラムとして動作しますが、exec を呼び出すことで、指定されたプログラムに置き換わります。
シェルから起動したコマンドが終了されると、シェルは終了ステータスを回収し、シェルの種類や設定によっては、終了ステータスを教えてくれたりします。
子プロセスが終了した場合に、そのあとどうするかはプログラムの要件によって異なります。たとえば、プリフォークモデル(prefork)のデーモンプロセスの場合、あらかじめ、forkをして、子プロセスを複数作成して、リクエストを子プロセスに任せ、子プロセスが終了した場合には、親プロセスが子プロセスを作り直す、といった実装があります。たとえば、Apache httpd server がそのような実装をされています。 httpd は、よく使われていたウェブサーバのプログラムです。 httpd は、マルチプロセスモードで利用できます。 httpd のデーモンの起動時に複数の子プロセスを生成して、子プロセスでソケットの接続を受け付け、子プロセスがクライアントにレスポンスを返します。
シェルスクリプトであれば、 コマンドの終了ステータスによって、そのあとの制御を変えたりします。
デーモンで子プロセスを管理する場合でも、シェルスクリプトでコマンドの終了ステータスで制御する場合でも、子プロセスを実行した親プロセスが、子プロセスの終了を検知しなければなりません。
Unix の世界では、基本的には、親プロセスが子プロセスのお葬式をする、と言われています。子プロセスは、親プロセスよりも先に死ぬことが前提になっています。そのため、子プロセスが終了すると、その情報をオペレーティングシステムが保有しています。その情報を親プロセスが回収しないと、オペレーティングシステムはその情報をいつまでも解放できません。
親プロセスが子プロセスが終了しているか、また、終了ステータスを回収するために使われるシステムコールが wait系のシステムコールです。 forkの対をなすシステムコールは waitだと思って構いません。
wait系システムコール
wait系のシステムコールには、以下のシステムコールがあります。
| システムコール | FreeBSD | Linux |
| wait | o | o |
| waitpid | o | o |
| waitid | o | o |
| wait3 | o | o |
| wait4 | o | o |
| wait6 | o | - |
2020年に確認したときには、 wait6 以外は、 FreeBSD と Linux には、同じシステムコールが実装されていました。
サンプルソース
いくつもの子プロセスを作成し、それぞれに処理をさせて、すべての子プロセスの処理を待つ場合のソースを書いてみました。親プロセスが子プロセスの数を覚えておいて、あといくつwaitしてあげなければならないのか、考えてもいいのですが、今回は、wait()とerrnoを使って判別します。
プログラムの流れは下記の通りです。
- multi_fork て複数の子プロセスを作成する
- multi_wait ですべての子プロセスの終了を待つ
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> // fork/wait
#include <unistd.h> // fork/sleep
#include <sys/wait.h> // fork/wait
#include <err.h>
#include <errno.h>
pid_t
Fork ()
{
pid_t pid;
pid = fork ();
if (-1 == pid)
{
warn ("can not fork");
}
return (pid);
}
void
child ()
{
(void) sleep (1);
(void) printf ("child: pid=%d\n", getpid () );
(void) fflush (stdout);
}
void
multi_fork ()
{
int i = 0;
int max = 10;
for (i = 0; i < max; ++i)
{
pid_t pid = Fork ();
if (-1 == pid)
{
break;
}
else if (0 == pid)
{
// child
child ();
exit (EXIT_SUCCESS);
/* NOTREACHED */
}
}
}
void
multi_wait ()
{
for ( ; ; )
{
pid_t pid;
int status = 0;
pid = wait (& status);
if (pid == -1)
{
if (ECHILD == errno)
{
// 子プロセスが存在しない
break;
}
else if(EINTR == errno)
{
continue;
}
// wait が失敗した
err (EXIT_FAILURE, "wait error");
}
(void) printf ("parent: child = %d, status=%d\n", pid, status);
}
}
int
main(int argc, char *argv[])
{
multi_fork ();
sleep (2); // 少し待つ
multi_wait ();
exit (EXIT_SUCCESS);
}
コンパイル方法
コンパイルするコマンドは、下記の通りです。cc multi_wait.c
実行例
実行方法は、下記の通りです。% ./a.out child: pid=22755 child: pid=22756 child: pid=22757 child: pid=22759 child: pid=22760 child: pid=22761 child: pid=22762 child: pid=22763 child: pid=22764 child: pid=22758 parent: child = 22764, status=0 parent: child = 22763, status=0 parent: child = 22762, status=0 parent: child = 22761, status=0 parent: child = 22760, status=0 parent: child = 22759, status=0 parent: child = 22758, status=0 parent: child = 22757, status=0 parent: child = 22756, status=0 parent: child = 22755, status=0
さいごに
Unixのプロセスの作成や子プロセスの終了後の処理方法やforkとwaitのシステムコールの理解は深まりましたでしょうか?これに関連する記事を紹介いたします。
forkシステムコールの使い方については、[2007-12-21-1] で解説しています。
waitシステムコールの使い方については、[2007-12-21-2] で解説しています。
waitシステムコールとwaitpidシステムコールについては、 [2007-12-23-2] で解説しています。
C言語のerrnoの実装(FreeBSD)については、 [2007-12-22-1] で解説しています。
UNIX C言語プログラミング プロセスの存在を調べる方法については、 [2008-06-17-2] で解説しています。
これらの記事を参考にしていただければ幸いです。
参照しているページ (サイト内): [2007-12-23-2]
スポンサーリンク
スポンサーリンク
いつもシェア、ありがとうございます!
もっと情報を探しませんか?
関連記事
最近の記事
- パナソニック ジェットウォッシャードルツ EW-DJ61-Wのホースの修理
- LinuxセキュリティモジュールIntegrity Policy Enforcement
- アマゾンのEcho Show 5を買ったのでレビューします
- アマゾンのサイバーマンデーはAlexa Echo Show 5が安い
- Android スマートフォン OnePlus 7T と OnePlus 7の違い
- Android スマートフォン OnePlus 7 をAndroid10にアップデートしてみた
- クレジットカードのバーチャルカードの比較のまとめ
- 活動量計 Xiaomi Mi Band 4を買ってみたのでレビュー
- Android スマートフォン OnePlus 7 のレビュー
- AliExpressでスマートフォンを買い物してみた
- パソコンのホコリ対策 レンジフードフィルターと養生テープ
- 80PLUS GOLDのPC電源ユニットAntec NeoEco 750 Goldのレビュー
- イギリスの付加価値税 VAT は払い戻しを受けられる
- イギリスのロンドンでスーツケースなど荷物を預けられる場所は
- イギリスのロンドンで地下鉄やバスに乗るならオイスターカードを使おう
- イギリスのヒースロー空港からロンドン市内への行き方
- 航空便でほかの航空会社に乗り継ぎがある場合のオンラインチェックイン
- SFC会員がANA便ではなくベトナム航空のコードシェアを試して解ったこと
- ベトナムの入国審査でeチケットの掲示が必要だった話
- シアトルの交通ICカードはオルカカード(Orca)です
人気のページ
- Windows7 IME 辞書ツールで単語の登録に失敗しました
- C言語 popen()でコマンドを実行して出力を読み込む
- Windows7で休止状態にする方法
- CentOS MySQLの起動、停止、再起動
- loggerコマンドでsyslogにエラーを出力する方法
- パソコンパーツの買取をしてくれる店のまとめ
- Java Mapの使い方 get(),put(),remove(),size(),clear()
- 楽天のRポイントカードを作ってみた
- iPhone 5 から iPhone 6 に乗り換えたのでレビュー
- netstatコマンドのステータスの意味
スポンサーリンク
過去ログ
2020 : 01 02 03 04 05 06 07 08 09 10 11 122019 : 01 02 03 04 05 06 07 08 09 10 11 12
2018 : 01 02 03 04 05 06 07 08 09 10 11 12
2017 : 01 02 03 04 05 06 07 08 09 10 11 12
2016 : 01 02 03 04 05 06 07 08 09 10 11 12
2015 : 01 02 03 04 05 06 07 08 09 10 11 12
2014 : 01 02 03 04 05 06 07 08 09 10 11 12
2013 : 01 02 03 04 05 06 07 08 09 10 11 12
2012 : 01 02 03 04 05 06 07 08 09 10 11 12
2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12
2006 : 01 02 03 04 05 06 07 08 09 10 11 12
2005 : 01 02 03 04 05 06 07 08 09 10 11 12
2004 : 01 02 03 04 05 06 07 08 09 10 11 12
2003 : 01 02 03 04 05 06 07 08 09 10 11 12