スポンサーリンク

このドキュメントの内容は、以下の通りです。

はじめに

パソコンでは、オペレーティングシステムが動作し、オペレーティングシステムの中でさまざまなソフトウェアが実行されます。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]

スポンサーリンク
スポンサーリンク
 
いつもシェア、ありがとうございます!


もっと情報を探しませんか?

関連記事

最近の記事

人気のページ

スポンサーリンク
 

過去ログ

2020 : 01 02 03 04 05 06 07 08 09 10 11 12
2019 : 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

サイト

Vim入門

C言語入門

C++入門

JavaScript/Node.js入門

Python入門

FreeBSD入門

Ubuntu入門

セキュリティ入門

パソコン自作入門

ブログ

トップ


プライバシーポリシー