Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

C++

C++のfork関数の構文や使い方について

投稿日:2022年1月16日 更新日:

 

<目次>

(1) C++のfork関数の構文や使い方について
 (1-1) fork()関数とは?概要や目的
 (1-2) fork()関数の構文
 (1-3) fork()関数のサンプルプログラム(疎通レベル)
 (1-4) forkでの親と子の待ち合わせ
 (1-5) (参考)fork()とforループを組み合わせて複数の子プロセスを起動する

(1) C++のfork関数の構文や使い方について

(1-1) fork()関数とは?概要や目的

「fork()」は子プロセスを起動するシステムコール(プログラム⇒OSに対しての指令)の関数です。並列処理などが求められるケースにおいて使用されます。

・fork()を実行すると、呼び出し元のプロセスを「複製」する事で新しいプロセスを生成します。
・その際に生成された新しいプロセスは「子プロセス」、呼び出し元のプロセスは「親プロセス」と呼ばれます。
・親プロセスと子プロセスはそれぞれ独立したメモリ領域で起動しますが、子プロセスの生成直後は親と子は全く同じ内容になっています(基本は完全なるコピー)。
 
・ただし、親と子は一部異なる点もあり、以下はその例です。
(例1)子プロセスは一意なPID(プロセスID)を持つ(親のPIDとも異なる)
(例2)子プロセスは親プロセスのメモリロック(※注1)を引き継がない。
(例3)リソース消費量(getrusage())や、プロセス起動時間(times())はゼロにリセットされる。
 
(※注1)参考:メモリロックとは?(mlock()・mlockall()関数)
RAMのアドレス空間(アドレスを保持する領域)の一部または全部をロックする事で、メモリのデータがスワップ領域(=ディスク上の仮想的なメモリ)にページング(スワップ領域への移動)される事を防ぎます。
 

(1-2) fork()関数の構文

(構文)

pid_t fork()
(構文説明)
・戻り値の型である「pid_t」はプロセスIDを扱うための型です(≒符号付き整数≒int)
・fork()関数が正常終了すると、起動した子プロセスのPIDを返却します。
・fork()関数が異常終了すると、「-1」を返却します。

目次にもどる

(1-3) fork()関数のサンプルプログラム(疎通レベル)

(サンプルプログラム)

#include <iostream>
//#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
  
    // fork()で子プロセスを生成(親と全く同じ動作)
    fork();
  
    cout << "Hello world Rainbow !\n" << endl;
    return 0;
}

(図131)

●実行結果

親プロセスで「Hello world~」出力後、子プロセスでも同様に出力するため2回表示されています。
(図132)
 

目次にもどる

(1-4) fork()forkでの親と子の待ち合わせ

プログラムにおいてfork()関数を使用する場合は、セットでwait()関数も使う必要があります。このwait()関数は親プロセス内で、子プロセスの処理の終了を待つために使用されます。一方で、子プロセスの処理を終了する際には、子プロセス内でexit()関数を実行します。

(図141)

●forkでの親と子の待ち合わせのサンプルプログラム

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main(void) {
  //# 子プロセスを起動
  pid_t pid = fork();
  //# pid==0、つまり子プロセスの場合
  if(pid == 0) {
    //# 子プロセスの親プロセスPIDと、自身のPIDを出力
    cout << "###[Child Process] (1)PPID: "<< getppid() <<" (2)PID: "<< getpid() << endl;
    //# 子プロセスの処理終了
    exit(EXIT_SUCCESS);
  }
  else if(pid > 0) {
    //# 親プロセスのPIDを出力
    cout << "###[Parent Process] (1)PID: "<< getpid() << endl;
    cout << "###[Parent Process] Wait for Child Process to finish. " << endl;
    //# 子プロセスの処理終了を待つ
    wait(NULL);
    cout << "###[Parent Process] Child process finished. " << endl;
  }
  else {
    cout << "###[Parent Process] Failed to create child process. " << endl;
  }
  return EXIT_SUCCESS;
}

(図151)①

●実行結果

子プロセスを生成後、親プロセスでは「子プロセスの終了待ち(wait)」を行います。

(図151)②

(結果例)

[admin@ik1-411-37776 tmp_rainbow]$ g++ -g -o ./fork_sample2 ./fork_sample2.cpp
[admin@ik1-411-37776 tmp_rainbow]$
[admin@ik1-411-37776 tmp_rainbow]$ ./fork_sample2
###[Parent Process] (1)PID: 12585
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 12585 (2)PID: 12586
###[Parent Process] Child process finished.

目次にもどる

(1-5) (参考)fork()とforループを組み合わせて複数の子プロセスを起動する

forループを使えば、必要な分だけ子プロセスを生成する事も出来ます。下記の例では子プロセスを4個生成しています。また、各子プロセスのPIDと、その親プロセスのPID(PPID)も表示しています。この時、全ての子プロセスは同じ親プロセスに所属します。

(サンプル)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main(void) {
  for(int i = 1; i <= 5; i++) {
    pid_t pid = fork();
 
    if(pid == 0) {
      //# 子プロセスの親プロセスPIDと、自身のPIDを出力
      cout << "###[Child Process] (1)PPID: "<< getppid() <<" (2)PID: "<< getpid() << endl;
      exit(0);
    }
    else  {
      //# 親プロセスのPIDを出力
      cout << "###[Parent Process] (1)PID: "<< getpid() << endl;
      cout << "###[Parent Process] Wait for Child Process to finish. " << endl;
      wait(NULL);
      cout << "###[Parent Process] Child process finished. " << endl;
    }
  }
 
  return EXIT_SUCCESS;
}

(図161)①

●実行結果

想定通り下記の4行1セット(親プロセスPID出力⇒子プロセス終了待ち⇒子プロセスPID出力⇒子プロセス終了判断)が、forkの回数分だけ発生しています。

###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: [子プロセスのPID]
###[Parent Process] Child process finished.

(結果例)
[admin@ik1-411-37776 tmp_rainbow]$ ./fork_sample3
###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: 1138
###[Parent Process] Child process finished.
###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: 1139
###[Parent Process] Child process finished.
###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: 1140
###[Parent Process] Child process finished.
###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: 1141
###[Parent Process] Child process finished.
###[Parent Process] (1)PID: 1137
###[Parent Process] Wait for Child Process to finish.
###[Child Process] (1)PPID: 1137 (2)PID: 1142
###[Parent Process] Child process finished.
 
<span style="font-size: 15px;">(図161)②</span>

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-C++

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

C++のunsigned char型とは?概要やsigned charとの違いもご紹介

<目次> (1) C++のunsigned char型とは?概要やsigned charとの違いもご紹介  (1-1) 概要  (1-2) サンプルプログラム①:疎通確認 (1) C++のunsign …

Valgrindの使い方や見方について(基礎編)

  <目次> (1) Valgrindの使い方や見方について(基礎編)  (1-1) メモリリークのチェックのやり方  (1-2) 「valgrind」コマンドの基本的な使い方  (1-3) …

C++のlocaltime関数がスレッドアンセーフである理由と使用上の注意点

  <目次> (1) C++のlocaltime関数がスレッドアンセーフである理由と使用上の注意点  (1-1) localtime関数がスレッドアンセーフである理由  (1-2) loca …

C++をLinuxでコンパイルする方法について

  <目次> (1) C++をLinuxでコンパイルする方法について  (1-1) STEP0:【事前準備】ターミナル機能の準備(ターミナル、Teraterm、Putty)  (1-2) S …

C++の共用体とは?構造体との違いや使用方法について

  <目次> (1) C++の共用体とは?構造体との違いや使用方法について  (1-1) 共用体とは?構造体との違いは?  (1-2) 共用体の定義方法、変数定義方法、初期化方法  (1-3 …

  • English (United States)
  • 日本語
Top