関数仕様
書式
1 2 3 4 5 |
#include <sys/types.h> #include <unistd.h> pid_t fork(void); |
引数
なし |
戻り値
正の値 | 子プロセスのプロセスID(forkに成功し、親プロセスに返される) |
0 | forkに成功し、子プロセスとして動く |
-1 | エラーが発生 |
1 2 3 4 5 6 7 8 9 |
pid = fork(); if (pid > 0) { // 親プロセス } else if (pid == 0) { // 子プロセス } else { // エラー } |
機能
- 呼び出し元プロセスを複製して新しいプロセスを生成する
※新しいプロセスは「子」プロセスと呼ばれ、呼び出し元プロセスは「親」プロセスと呼ぶ - 新たなプログラムを実行するために fork を呼出す場合、execve と組み合わせて使用する
関連
- execve() については、「execve関数の使い方」にまとめています。
- POSIX仕様に関して「fork()とexecve()の間で余計なことをするな」にまとめています。
注意すること
注意ポイント①
子プロセスは、親プロセスが持つファイルディスクリプターのコピーを引き継ぐ。
fork の機能で説明した通り、fork は呼出し元プロセスの複製を生成します。
fork を呼出す前に pipe や socketpair などでプロセス間通信の準備を行う場合は便利な仕組みです。
しかし、fork を呼出すプロセスがTCP ソケットなどを使用している場合は注意が必要です。
TCP の下位プロトコルは OS 側で制御されます。
ソケットが閉じられたことを知らせる FIN は、ソケットが閉じられた時に送信されますが、
意図せず複製が生成された場合、FIN が送信されないことになり相手側で通信断を検知できなくなります。
参考
意図しないコピーの引継ぎを防ぐには、O_CLOEXEC を指定してオープンします。
「【C言語】open関数の使い方」もご覧ください。
注意ポイント②
子プロセスを終了させる場合は、_exit() システムコールを使うこと。
子プロセスを終了する際には、main() 関数を return したり、標準Cライブラリの exit() を呼出すのではなく、
システムコールの _exit() を呼出す必要があります。
先ほどの「注意ポイント①」にも関連しますが、標準Cライブラリの exit() では、
親プロセスから引き継いだ入出力ライブラリ(stdio.h)のバッファをクリアする処理が動いてしまいます。
参考
exit 関数については、「【C言語】exit関数の正しい使い方」も参考にどうぞ。
サンプルプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void mask_sigchld (void) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigset, NULL); } static void unmask_sigchld (void) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); } int main(int argc, char *argv[]) { pid_t pid; /* 複数子プロセスの終了を待つ場合など、 fork() 処理中は SIGCHLD をブロックする*/ mask_sigchld(); pid = fork(); if (pid > 0) { /* 親プロセス */ write(1, "parent process\n", 15); } else if (pid == 0) { /* 子プロセス */ write(1, "child process\n", 14); /* 意図しない fd の複製によるバグを防ぐ */ /* 0=stdin, 1=stdout, 2=stderr */ /* 1024= ulimit -n の結果 */ int i; for(i = 3; i < 1024; i++) { close(i); } _exit(0); } else { /* エラー */ perror("fork"); } unmask_sigchld(); return 0; } |