無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

forkのスタックの取り扱い


forkで子プロセスを作成すると、親プロセスのメモリー空間を継承するが、スタックも例外でない。行き着くところ、スタックもメモリアクセスにおいて、通常メモリと変わりなく、あたりまえのことのようで、でも、ちょっと検証してみて、改めて新しい発見をしたような気分になりましたので。

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 (コアダンプ)の表示がないのは、そのシグナルを親に送信しているからだと・・・。

最終更新 2012/08/21 17:20:35 - north
(2012/08/21 17:20:35 作成)