NetBSD-current /sbin/initが起動されるまで(1)

main()で一通りの初期化が終わって/sbin/initを起動する準備が整ったら、start_init()がfork()される:

        if (fork1(l, 0, SIGCHLD, NULL, 0, start_init, NULL, NULL, &initproc))
                panic("fork init");

start_init()の流れを順に見ていく。

        /*
         * Now in process 1.
         */
        strncpy(p->p_comm, "init", MAXCOMLEN);

proc構造体のプロセス名のフィールドにinitと書き込んでいる。

        /*
         * Wait for main() to tell us that it's safe to exec.
         */
        mutex_enter(proc_lock);
        while (start_init_exec == 0)
                cv_wait(&lbolt, proc_lock);
        mutex_exit(proc_lock);

main()がstart_init_execを1にするまで待つ。
一方、mainの最後には以下のようなコードがあってこれがstart_init_execを1にする:

        /*
         * Okay, now we can let init(8) exec!  It's off to userland!
         */
        mutex_enter(proc_lock);
        start_init_exec = 1;
        cv_broadcast(&lbolt);
        mutex_exit(proc_lock);

ユーザプロセスのためのスタックをUVMから確保する。

        /*
         * Need just enough stack to hold the faked-up "execve()" arguments.
         */
        addr = (vaddr_t)STACK_ALLOC(USRSTACK, PAGE_SIZE);
        if (uvm_map(&p->p_vmspace->vm_map, &addr, PAGE_SIZE,
                    NULL, UVM_UNKNOWN_OFFSET, 0,
                    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_COPY,
                    UVM_ADV_NORMAL,
                    UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW)) != 0)
                panic("init: couldn't allocate argument space");
        p->p_vmspace->vm_maxsaddr = (void *)STACK_MAX(addr, PAGE_SIZE);

boothowtoがinitのPATHを聞くようにしている時にプロンプトを出す処理。

                if (boothowto & RB_ASKNAME) {
                        printf("init path");
                        if (initpaths[ipx])
                                printf(" (default %s)", initpaths[ipx]);
                        printf(": ");
                        len = cngetsn(ipath, sizeof(ipath)-1);
                        if (len == 0) {
                                if (initpaths[ipx])
                                        path = initpaths[ipx++];
                                else
                                        continue;
                        } else {
                                ipath[len] = '\0';
                                path = ipath;
                        }
                } else {
                        if ((path = initpaths[ipx++]) == NULL)
                                break;
                }

ucpがさっきのスタックだ。

                ucp = (char *)USRSTACK;

boothowtoを元に引数を作っている。

                /*
                 * Construct the boot flag argument.
                 */
                flagsp = flags;
                *flagsp++ = '-';
                options = 0;

                if (boothowto & RB_SINGLE) {
                        *flagsp++ = 's';
                        options = 1;
                }

さっきのスタックからメモリを確保して第一引数を積んでいる。
copyout()はカーネル→ユーザへのmemcpy()。
実際、memcpy()と何が違うんだろう・・・?

                /*
                 * Move out the flags (arg 1), if necessary.
                 */
                if (options != 0) {
                        *flagsp++ = '\0';
                        i = flagsp - flags;
#ifdef DEBUG
                        printf("init: copying out flags `%s' %d\n", flags, i);
#endif
                        arg1 = STACK_ALLOC(ucp, i);
                        ucp = STACK_MAX(arg1, i);
                        (void)copyout((void *)flags, arg1, i);
                }

同様にファイル名もコピー。

                /*
                 * Move out the file name (also arg 0).
                 */
                i = strlen(path) + 1;
#ifdef DEBUG
                printf("init: copying out path `%s' %d\n", path, i);
#else
                if (boothowto & RB_ASKNAME || path != initpaths[0])
                        printf("init: trying %s\n", path);
#endif
                arg0 = STACK_ALLOC(ucp, i);
                ucp = STACK_MAX(arg0, i);
                (void)copyout(path, arg0, i);

ファイル名、引数を構造体に詰めてexecveシステムコールに渡して一丁あがり。

                /*
                 * Move out the arg pointers.
                 */
                ucp = (void *)STACK_ALIGN(ucp, ALIGNBYTES);
                uap = (char **)STACK_ALLOC(ucp, sizeof(char *) * 3);
                SCARG(&args, path) = arg0;
                SCARG(&args, argp) = uap;
                SCARG(&args, envp) = NULL;
                slash = strrchr(path, '/');
                if (slash)
                        (void)suword((void *)uap++,
                            (long)arg0 + (slash + 1 - path));
                else
                        (void)suword((void *)uap++, (long)arg0);
                if (options != 0)
                        (void)suword((void *)uap++, (long)arg1);
                (void)suword((void *)uap++, 0); /* terminator */

                /*
                 * Now try to exec the program.  If can't for any reason
                 * other than it doesn't exist, complain.
                 */
                error = sys_execve(l, &args, retval);
                if (error == 0 || error == EJUSTRETURN) {
                        KERNEL_UNLOCK_LAST(l);
                        return;
                }

次はexecveについてみていこう。