vfork


Rev.3を表示中。最新版はこちら

vforkと言うのがある。たぶん今となっては使うことはないのでは・・・(実務は良くわかりませんが。)これは子プロセスが終了/execするまで実質、親プロセスは動作しない。以下はその違いを検証するのサンプルで、vforkの場合、子プロセスが終了するまで、親プロセスは動作していないことが見て取れる。なお、forkの場合は親プロセスがまず動作して、スケジューラにより、子プロセスも起動している。

なお、vforkでは、スレッドのようにstatic変数aを共有している。ただしスレッドのように他のリソースは共有しない。これは共有することが目的でなく、vforkの目的上、共有せざるおえないからである。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <err.h>  

int     a = 0;
int main()
{
       pid_t   pid;
       int     i;

       pid = vfork();
//       pid = fork();
       if (pid == 0) {
               a = 1;
               for (i = 0; i < 5; i++) {
                       printf("child %d\n", i + 1);
                       sleep(1);
               }
               exit (EXIT_SUCCESS);
       }
       else {
               printf("*data %d\n", a);
               for (i = 0; i < 5; i++) {
                       printf("parent %d\n", i + 1);
                       sleep(1);
               }
               exit (EXIT_SUCCESS);
       }
}
vfork()で子プロセスを作成
[kitamura@localhost ]$ ./a.out
child 1
child 2
child 3
child 4
child 5
*data 1
parent 1
parent 2
parent 3
parent 4
parent 5
fork()で子プロセスを作成
[kitamura@localhost test]$ ./a.out
*data 0
parent 1
child 1
parent 2
child 2
parent 3
child 3
parent 4
child 4
parent 5
child 5
vforkはUNIX時代の過去の産物らしい。そのforkの実装では、子プロセスのメモリも確保し、その空間で動作するようになっていた。そうなると、forkの動作が重くなる。forkのlinuxの実装は、メモリー空間は確保しない。親プロセスのそれを利用する。そしてコピーオンライトにより、書き込み処理毎に順次確保していく。

コピーオンライトは、MMUのページング機能によるハードの助けをかりて実装される手法である。従ってMMUの実装していない当時のシステムでは、このコピーオンライトによる遅延割り当てができなかった。(たぶん)そのような環境下でのforkの負荷の軽減すべく、子プロセスは親のメモリー空間で動作させようとするものだ。従って子プロセスが、exec/exitで自身メモリー空間を空け渡すまで、親プロセスは動作する事ができないのだ。通常forkはexecで別のプロセスに置き換えられる、その時新しくメモリを確保することになり、親プロセスも動作するようになる。

実装

vforkは、CLONE_VFORK | CLONE_VM | SIGCHLDで、do_fork()をコールする。CLONE_VMが親プロセスメモリ空間で動作するゆえんである。
int sys_vfork(struct pt_regs *regs)
{
       return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
                      NULL, NULL);
}
CLONE_VFORKでdo_forkがコールされると、子プロセスのp->vfork_done = &vforkに、struct completionが設定される。そしてwake_up_new_taskで子プロセスを起床させ、wait_for_vfork_doneで子プロセスがメモリ空間を開放するまで、親プロセスはウエイトすることになる。
long do_fork(unsigned long clone_flags,
             unsigned long stack_start,
             struct pt_regs *regs,
             unsigned long stack_size,
             int __user *parent_tidptr,
             int __user *child_tidptr)
{
       struct task_struct *p;
       int trace = 0;
       long nr;
   :
   :
       if (!IS_ERR(p)) {
               struct completion vfork;

               trace_sched_process_fork(current, p);

               nr = task_pid_vnr(p);

               if (clone_flags & CLONE_PARENT_SETTID)
                       put_user(nr, parent_tidptr);

               if (clone_flags & CLONE_VFORK) {
                       p->vfork_done = &vfork;
                       init_completion(&vfork);
                       get_task_struct(p);
               }

               UTRACE_HOOK(current, CLONE, report_clone(clone_flags, p));

               wake_up_new_task(p);

               if (clone_flags & CLONE_VFORK)
                       UTRACE_HOOK(current, CLONE, finish_vfork(current));
               if (unlikely(trace))
                       ptrace_event(trace, nr);

               if (clone_flags & CLONE_VFORK) {
                       if (!wait_for_vfork_done(p, &vfork))
                               ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
               }
       } else {
               nr = PTR_ERR(p);
       }
       return nr;
}
補足
wait_for_vfork_doneで子プロセスのメモリ空間を開放を待つ間も、親プロセスに実行権が移譲されるも、親プロセスのウエイトしているパスは、wait_for_vfork_done関数内である。ここで子プロセスのメモリ開放をチェックし、まだのようだと実行権を他に譲ることになる。

最終更新 2012/08/11 17:16:13 - north
(2012/08/11 16:52:43 作成)


検索

アクセス数
3586025
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。