fork () is evil; vfork () - good; afork () is better; clone () - stupid


popen() ( , API) clone(2), issue vfork(2) posix_spawn() . , Linux clone(2). , , , - : , , .





, .





- , Unix, , fork(2) fork-exec , Windows , exec*() _spawn*(), .





, fork(2) . vfork(2)



, , . vfork(2)



, , ( ).





, .





, fork(2) - , , . vfork(2)



. vfork(2)



fork(2)



, , , , , . , vfork(2)



! , , : exec*(2)



, _exit(2)



.





3BSD vfork(2)



, 4.4BSD , . . 4.4BSD . : vfork(2)



, fork(2)



- , . , fork(2)



, ( , ). , , . . , , seed’ , (, JVM), RSS . , fork(2)



, (, shell).





, , fork(2)



- . , fork-safety! fork-safety thread-safety (), fork-safe , thread-safe. fork-safety: .





( , : , fork(2)



, , , . , fork-unsafe , , fork(2)



. vfork(2)



. Windows CreateProcess()



_spawn()



, .)





, fork(2)



? , : CreateProcess*()



, _spawn()



posix_spawn()



, , , , fork()



exec()



, , shell. fork()



exec()



API, : ! fork(2)



Unix kernel-land user-land, - , , . , Unix , . , . , .





vfork()



fork()



!





vfork()



: ( , , vfork()



) , () , exec()



_exit()



. ( - , vfork(2)



- , . - main()- .) - , , , . vfork()



/ . afork()



avfork()



. afork()



pthread_create()



: , .





, vfork()



, , , , exit/exec, . Linux, , - vfork()



, . , , IIRC ( , IMO).





afork()



API popen()



I/O . - , , ( ) EOF, / EPIPE / SIGPIPE, popen()



.





forkx()/vforkx() Illumos , afork()



- :





pid_t afork(int (*start_routine)(void *), void *arg);
pid_t aforkx(int flags /* FORK_NOSIGCHLD / FORK_WAITPID */, int (*fn)(void *), void *arg);
      
      



, afork()



Linux: clone(<function>



, <stack>



, CLONE_VM | CLONE_SETTLS



, <argument>



). ( , SIGCHLD , , popen()



, , pclose()



. Illumos.)





- afork ()



( Illumos forkx()



) POSIX, pthread_create()



, vfork()



, afork()



. taskq, , , afork()



. afork()



, , PID, taskq pre-vfork ( !), , - :





int emulated_afork(int (*start_routine)(void *), void *arg, void (*cb)(pid_t) /*   NULL */);
      
      



pre-vfork, PID afork()



, pre-vfork



: pthread_cond_wait()



, , . ( vfork()



. , read()



write()



vfork()



) :





//  ,   vfork()       ,   vfork(),    . ,   Linux.
//      ,    avfork() . ,  Linux    clone(2).

static struct avfork_taskq_s { /*  */ ... } *avfork_taskq;
static void
avfork_taskq_init(void)
{
    // ,     
    ...
}

//  taskq ,  ,  
// taskq    
static void *
worker_start_routine(void *arg)
{
    struct worker_s *me = arg;
    struct job_s *job;
    

    //    pthread_cond_signal()    ,    .
    avfork_taskq_add_worker(avfork_taskq, me);

    do {
        if ((job = calloc(1, sizeof(*job))) == NULL ||
            pipe2(job->dispatch_pipe, O_CLOEXEC) == -1 ||
            pipe2(job->ready_pipe, O_CLOEXEC) == -1 ||
            (pid = vfork()) == -1) {
            avfork_taskq_remove(avfork_taskq, me, errno);  // !
            break;
        }
        if (pid != 0) {
            //     exit  exec
            if (job->errno)
                //      
                reap_child(pid);
            else
                //    ;  ,        .
                //            ,    .
                avfork_taskq_record_child(avfork_taskq, me, job, pid);
                

            if (avfork_taskq_too_big_p(avfork_taskq))
                break//   taskq
            continue;
        }
        

        //  
      
        //  ,     read(2), write(2), _exit(2)  start_routine   avfork(). avfork() start_routine()    ,    ,     ,      vfork().       C     -    : ,  , , RTLD  ..   , dup2(2), close(2), sigaction(2),   , exec(2)  _exit(2)     start_routine(),      posix_spawn(),   popen(),  system ()  ..

        //   ,        taskq.

        //  

        if (net_read(me->dispatch_pipe[0], &job->descr, sizeof(job->descr)) != sizeof(job->descr)) {
            job->errno = errno ? errno : EINVAL;
            _exit(1);
        }
        job->descr->pid = getpid();  //  pid,        
        if(net_write(me->ready_pipe[1], "", sizeof("")) != sizeof(""))  {
            job->errno = errno;
            _exit(1);
        }


        // 
        _exit(job->descr->start_routine(job->descr->arg));
    } while(!avfork_taskq->terminated); // ,    atexit()
    return NULL;
}
pid_t
avfork(int (*start_routine)(void *), void *arg)
{
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    struct worker_s *worker;
    struct job_descr_s job;
    struct job_descr_s *jobp = &job;
    char c;

    // avfork_taskq_init()  ,   ,   .     N  ,   ,    ,  taskq.get_worker()   pthread_cond_wait(),     .
    pthread_once(&once, avfork_taskq_init);


    //  
    memset(&job, 0, sizeof(job));
    job.start_routine = start_routine;
    job.arg = arg;
    worker = avfork_taskq_get_worker(avfork_taskq); //  ,   ;    
    //    .   ,   ,  pre-vfork()        .    ,      vfork().         ,     .
    //
    //  taskq   ,        ,   ,     .
    // ,    ,   ,   ,    ( worker_start_routine()).
    if (net_write(worker->dispatch_pipe[1], &jobp, sizeof(jobp)) != sizeof(jobp) ||
        net_read(worker->ready_pipe[0], &c, sizeof(c)) != sizeof(c))
        job.errno = errno ? errno : EINVAL;
    // 
    (void) close(worker->dispatch_pipe[0]);
    (void) close(worker->dispatch_pipe[1]);
    (void) close(worker->ready_pipe[0]);
    (void) close(worker->ready_pipe[1]);


    if (job.errno)
        return -1;
    return job.pid; //   , PID   pid
}
      
      



, clone(2) - . .  clone (2) POSIX, POSIX. , fork()



, , , , avfork()



! avfork()



. NPTL .





Linux - , pthread



Linux. Linux Solaris/SVR4, BSD libsocket STREAMS . API API .





clone()



- , /jail’, : Linux /jail’, clone(2)



, . clone(2)



, ... clone(2)



, .





Linux fork()



, vfork()



, avfork()



, thread_create()



container_create()



. , ( minder/init). , , /// , . clone(2)



, , .





, , «, - /jail’, », . /jail’, Linux /jail’. , . /jail’ , . - , , , clone(2) . , « » ( Linux : , C, , shell’, , - ). .





. . , fork()



, vfork()



, afork()



( ) clone()



( !). , 4.4BSD vfork()



( ). , , , , .






« ».








All Articles