見出し画像

続C言語教室 - 第24回 forkでプロセスの起動

自分でコードを書かなくても良くある機能というか処理は、既にアリものが何らかの形で用意されていることも多いです。それがソースコードであれば自分のコードと一緒にコンパイルして実行ファイルを作れば良いですし、ライブラリであればそれをリンクして実行ファイルを作れば良いわけです。

さて、その機能がバイナリしか用意されていないこともあります。まあバイナリというか既にコマンドとして用意されているような機能を呼び出したい場合などには、プログラムからそれを呼び出したいこともあります。もちろん最初から何らかの理由で別のプロセスとして実行したほうが都合が良いということありますしね。

というところで、プログラムからプログラムを呼び出す方法として「プロセスを起動する」にはどのようにすれば良いのかが今回のネタです。プロセスの扱いはOSによって大きく異なりますが、まずはC言語のお里であるlinuxなどのUNIX系での方法を調べてみましょう。

パッと思いつく方法としては、何らかのシステムコールに実行ファイルの名前を指定してプロセスを起動すれば良さそうにも思います。実際にPOISXにはsystemという関数も用意されていて、これを使ってプログラムを走らせることも出来ます。但しこの場合はシェルから起動したときのような引き数を渡すことは出来るものの、実行結果はプログラムの終了ステータス(mainのreturn値)を以外に受け取る方法はなく、これではせいぜい正常に終わったかくらいしかわかりません。あまり便利ではないので、もう少し込み入った方法でプロセスを起動するのが一般的です。

最初にプロセスをコピーして2つにする fork という関数を呼び出します。fork を呼び出すとプロセスはコピーされ、同じプログラムが親と子の2つのプロセスとなり、それぞれで実行が継続されます。同じプログラムなので、自分が親なのか子なのかは fork の戻り値で区別します。戻り値が0であれば自分は子供で、0以外であれば(但し-1の時はエラー)自分は親であることがわかります。後はそれぞれで実行が継続されるので親、子それぞれで実行したい処理を書いていけば良いわけです。

#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <unistd.h>

int main(void) {
  printf(”start(pid:%d,ppid:%d).\n”, getpid(), getppid());
  pid_t pid = fork();
  if (pid == -1) {
    fprintf(stderr, “can’t fork.\n”);
    return 1;
  }
  if (pid == 0) {
    // child
    printf(”child(pid:%d,ppid:%d).\n”, getpid(), getppid());
  else {
    // parent
    printf(”parent(pid:%d,ppid:%d).\n”, getpid(), getppid());
  }
  return 0;
}

start(pid:2761,ppid:1946).
parent(pid:2761,ppid:1946);
child(pid:2762,ppid:2761).

実行結果 ※idの値は実行する度に変わる。parentの行とchildの行は逆になることもある。

forkを実行した時点でプロセスはコピーされるので、それまでに使っていた変数もコピーされますし、開いていたファイル(記述子)などもそのまま使えます。もちろんコピーした後に変更した値はそれぞれになるのですが、標準入出力なども共有することになるのは忘れないでください(実はココがポイント)。

もちろん、forkだけで親子それぞれの処理を書いていくことも出来るのですが、これでは他のプログラムを起動したことにはなりませんね。よくある使い方としては、forkしてから子供側で、続けてexecを呼ぶことで自分の実行しているプログラムを引き数で指定した実行ファイルに「置き換えて」実行を続けます。この時の引き数の型を何種類かから選ぶことが出来るので、exec には execlであるとかexecv、execleなどなどがあるのですが基本的には同じものです。何だ system と同じじゃないかと思わなくも無いのですが、ここで標準入出力を繋ぎ変えてからexecしたりも出来ますし、歴史的経緯もあって、この方法が一般的にはなっています。

UNIX の C言語:forkとexec

execv(3) - Linux man page

https://linux.die.net/man/3/execv

その「繋ぎ変える」って何よ?とは思うのかもしれませんが、そこからは次回としましょう。

ヘッダ画像は、フォトショのAI生成の具合が悪かったので久しぶりにCoPilot君に描いて貰いました。

#C言語 #プログラミング #プログラミング講座 #C言語教室 #プロセス起動 #プログラム起動 #fork #exec  


いいなと思ったら応援しよう!

kzn
頂いたチップは記事を書くための資料を揃えるために使わせていただきます!