forkのスタックの取り扱い
forkで子プロセスを作成すると、親プロセスのメモリー空間を継承するが、スタックも例外でない。行き着くところ、スタックもメモリアクセスにおいて、通常メモリと変わりなく、あたりまえのことのようで、でも、ちょっと検証してみて、改めて新しい発見をしたような気分になりましたので。
forkシステムコールは、親プロセス(forkを呼び出したプロセス)の、レジスタを引数としてコールされ、子プロセスのスタック位置を、親プロセスのスタック位置となるよに、do_fork()をコール。従ってdo_forkで作成された子プロセスと親プロセスのスタック位置は、同じということであり、またそれまでのスタック内容も引き継いでもいるわけです。(スタックについてもコピー・オン・ライトが適用されます。)
以下は上記のことを検証するサンプルです。
./parent 2の実行結果で、Segmentation fault (コアダンプ)の表示がないのは、そのシグナルを親に送信しているからだと・・・。
forkシステムコールは、親プロセス(forkを呼び出したプロセス)の、レジスタを引数としてコールされ、子プロセスのスタック位置を、親プロセスのスタック位置となるよに、do_fork()をコール。従ってdo_forkで作成された子プロセスと親プロセスのスタック位置は、同じということであり、またそれまでのスタック内容も引き継いでもいるわけです。(スタックについてもコピー・オン・ライトが適用されます。)
int sys_fork(struct pt_regs *regs)
{
return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
}
これは、親プロセスがforkをコールするまでに、ある関数群を経由して、呼ばれたとすると、子プロセスは、親と別プロセスと動作しながらも、それらのコール群を継承しているということです。また、1プロセスに割り当てられるスタックサイズは制限があり、親が消費したスタックサイズも継承するということです。以下は上記のことを検証するサンプルです。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define SIZE 7*1024*1024
static void myget_sp(const char* msg);
void main(int argc, char** argv)
{
int sel, ret;
char dummy[SIZE];
sel = atoi(argv[1]);
if (sel == 1) {
loop_fun("parent");
}
ret = my_fork(sel);
printf("end 1 from parent/2 from child:%d\n", ret);
}
int my_fork(int sel)
{
pid_t pid;
if (sel == 0) {
myget_sp("parent");
}
pid = fork();
if (!pid) {
if (sel == 0) {
myget_sp("child");
}
if (sel == 2) {
loop_fun("child");
}
return 2;
}
return 1;
}
int cnt_loop = 0;
int loop_fun(const char* msg)
{
printf("%s: %d\n", msg, cnt_loop++);
loop_fun(msg);
}
static void myget_sp(const char* msg)
{
short int sp;
asm ("movw %%sp, %%ax;"
"movw %%ax, %0;"
:"=r"(sp)
:
:"%ax");
printf("sp %s:%x\n", msg, sp);
}
プロセスのスタックサイズを確認。私の場合8Mということです。[kitamura@localhost test]$ ulimit -s 8192親プロセス/子プロセスとも同じスタックポインタで、 my_forkの返りの結果は、子プロセスから復帰したことが分かり、従って子プロセスもスタック内容を継承していることになる。
[kitamura@localhost test]$ ./parent 0 sp parent:ffff8640 end 1 from parent/2 from child:1 [kitamura@localhost test]$ sp child:ffff8640 end 1 from parent/2 from child:2親プロセスが消費したスタックの子プロセスへの影響。親/子とも同じサイズのスタックを有していることが分かる。なお子プロセスが起動して後の消費したスタックは、コピーオンライトで子プロセス独自のスタックとなる。はず。
kitamura@localhost test]$ ./parent 1 : : parent: 32580 parent: 32581 parent: 32582 parent: 32583 Segmentation fault (コアダンプ) [kitamura@localhost test]$ ./parent 2 : : child: 32546 child: 32547 child: 32548 child: 32549
補足
execv毎に、スタックも含めてメモリが確保され、そこでの新規スタック位置が設定されるため、上記結果は毎回微妙に異なる。従ってexecvするとそのプロセスのスタックには最大のスタックが割り当てられることになる。./parent 2の実行結果で、Segmentation fault (コアダンプ)の表示がないのは、そのシグナルを親に送信しているからだと・・・。





