Open3

WasmLinux: vfork → execveの実装(未遂)

okuokuokuoku

というわけで山場だ。。 "v"fork → execve の流れを実装する。vforkとexecveでスクラップ分けた方が良いかな。。

vforkはforkの厳密なサブセットで、NOMMUなシステムでは唯一のfork(2)風機構となっている。ただマジでNOMMU耐性を考えるなら posix_spawnを使った方が良い。vforkの制約は通常の人間には非常に理解しづらい。

https://www.jpcert.or.jp/sc-rules/c-pos33-c.html

okuokuokuoku

テストプログラム

適当に書く。

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

int
main(int ac, char** av, char** envp){
    pid_t pid;
    int i,r;
    const char* next[] = {"dummy", "u", "m", 0};

    /* Dump args */
    i = 0;
    while(av[i]){
        fprintf(stderr, "[%d/%d]: %s\n", i, ac, av[i]);
        i++;
    }

    if(ac >= 2 && av[1][0] == 'u'){
        printf("execve-ed. my pid is %d\n", getpid());
        return 0;
    }else{
        pid = vfork();
        if(! pid){
            /* child process */
            printf("Going to execve()...(I'm %d)\n", getpid());
            r = execve(av[0], next, NULL);
            printf("Should not reach here %d,%d\n", r, errno);
        }else{
            /* parent process */
            printf("Forked to %d\n", pid);
            return 0;
        }
    }

    return 0;
}

無限再帰しないように適当に引数を渡すようにする。実行すると:

$ ./a.exe
[0/1]: ./a
Forked to 1858
Going to execve()...(I'm 1858)
[0/3]: dummy
[1/3]: u
[2/3]: m
execve-ed. my pid is 1858

のような感じに出力される。

okuokuokuoku

さくせん

setjmp longjmp で実装する。

  1. vfork が呼ばれる
  2. setjmp する
  3. 新規プロセスコンテキストを作成し、カレントスレッドのコンテキストを差し替え
  4. 一旦ゼロを返して execve されるまで実行する ★ ← コレがダメ
  5. evecve が呼び出されたら、新規ホストスレッドを生成し、3.で作成したコンテキストはそちらで使うようにし、カレントスレッドのコンテキストを元に戻す
  6. longjmp(新規プロセスのPID) する
  7. setjmp の戻り値を戻して実行継続する

残念ながらコレはC++例外では実装できず、C標準のsetjmp/longjmpが必要になる。

これダメじゃん。。 setjmp を呼出した関数から 戻ってはいけない 。vforkにも同じ制約があるが vfork → setjmp をマクロにしないといけない。

いやまぁもちろんAsyncifyすれば良いだけだけど、このためだけにパフォーマンスペナルティ払うのもちょっとなぁ。。