第3章プロセス管理
2段階のプロセス生成
Linuxにおけるプロセス生成の目的
同じプログラムの処理を複数のプロセスに分けて処理する
全く別のプログラムを生成する
fork()
同じプログラムを複数のプロセスに分けて処理する
発行したプロセスを元に、新たにプロセスを一つ生成する。
生成元が親プロセスで生成先が子プロセス
生成方法は以下の通り
①子プロセス用メモリ領域を作成して、親プロセスのメモリをコピーする。
②親プロセスと子プロセスは違うコードを実行するようになり分岐する。これには、fork()関数の戻り値が、親プロセスと子プロセスで異なることを利用する。
code:fork.c
static void child()
{
printf("I'm child! my pid is %d.\n", getpid());
exit(EXIT_SUCCESS);
}
static void parent(pid_t pid_c)
{
printf("I'm parent! my pid is %d and the pid of my child is %d. \n", getpid(), pid_c);
exit(EXIT_SUCCESS);
}
int main(void)
{
pid_t ret;
ret = fork();
if(ret == -1)
err(EXIT_FAILURE, "fork() failed.");
if(ret == 0){
child();
} else {
parent(ret);
}
err(EXIT_FAILURE, "should reach here.");
}
I'm parent! my pid is 26249 and the pid of my child is 26250.
I'm child! my pid is 26250.
プロセスIDの26490のプロセスが分岐して、新規にプロセスIDが26250のプロセスが生成され、fork()の発行後に処理が分岐しているのがわかる。
execve()
全く別のプログラムを生成する。
プロセス数が増えるのではなく、あるプロセスを別のプロセスで置き換える。
①実行ファイルを読み出して、プロセスのメモリマップに必要な情報を読み出す。
②現在のプロセスのメモリを新しいプロセスのデータで上書きする。
③新しいプロセスの最初の命令から実行開始する。
Linuxの実行ファイルはEexecutable Linkable Format(ELF)というフォーマットを使用する。
ELFの各種情報を取得するのはreadelf -h /bin/sleep
Entry point addressがこのプログラムのエントリポイント
コードとデータのファイル内オフセット、サイズ、開始アドレスを得るのはreadelf -S /bin/sleep
出力は2行で1組
数値は全て16進数
1行目の第2フィールドが.textなのがコード領域の情報、.dataなのがデータ領域の情報
各種情報はそれぞれの組みの以下の場所を見ればわかる
メモリマップ開始アドレス:1行目の第4フィールド
ファイル内オフセット:1行目の第5フィールド
サイズ:2行目の第1フィールド
プログラム実行時に作成されたプロセスのメモリマップは/proc/{pid}/mapsファイルによって得られる。
全く別のプロセスを新規作成する場合は、親となるプロセスからfork()を発行し、復帰後に子プロセスがexec()を呼ぶことが多い。
①プロセスを新規作成する。
②親プロセスはecho helloプログラムを生成した後に自身のプロセスIDと子プロセスIDを出力して終了する。
code:fork-and-exec.c
static void child()
{
char *args[] = {"/bin/echo", "hello", NULL};
printf("I'm child! my pid is %d.\n", getpid());
fflush(stdout);
execve("/bin/echo", args, NULL);
err(EXIT_FAILURE, "exec() failed.");
}
static void parent(pid_t pid_c)
{
printf("I'm parend!my pid is %d and the pid of my child is %d.\n", getpid(), pid_c);
exit(EXIT_SUCCESS);
}
int main(void)
{
pid_t ret;
ret = fork();
if(ret == -1)
err(EXIT_FAILURE, "fork() failed.");
if(ret == 0){
child();
} else {
parent(ret);
}
err(EXIT_FAILURE, "shouldn't reach here.");
}