This is an example only.  It takes a *BUNCH* of arcane assembly to make
    it work.

    I would suggest *EXTREME* caution when using a completely cloned address
    space such as RFPROC|RFMEM generate.  Normal library calls cannot be made
    by the child safely.   Thread libraries will also not work since they
    expect the pthread model.

    This is not portable.  The assembly is designed for ELF libraries.

                                        -Matt


/*
 * FTEST.C
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void fubar(void *data);

int
main(int ac, char **av)
{
    pid_t pid;
    int stackSize = 64 * 1024;
    char *stack = malloc(stackSize);
    volatile int v = 0;

    printf("parent pid %d\n", (int)getpid());

    fflush(stdout);
    pid = ffork(fubar, &v, stack, stackSize);
    printf("child pid returned to parent %d\n", pid);

    /*
     * If stack is not split, then child should be able to 
     * increment v.
     */
    for (;;)  {
        sleep(1);
        printf("%d\n", v);
    }
    exit(1);
}

void
fubar(void *data)
{
    int *pdata = data;

    for (;;)
        ++*pdata;
    _exit(0);
}


/*
 * FFORK.S
 */

        .globl  ffork

        .text
/*
 * ffork(func, data, stack, stackSize)
 */

ffork:
        /*
         * Setup stack frame for new stack and store in register
         * which will not be destroyed by call to rfork, %ebx.
         *
         *      newsp -> func
         *               data
         */
        pushl   %ebx
        movl    12+4(%esp),%ebx
        addl    16+4(%esp),%ebx
        subl    $16,%ebx        /* newsp = %ebx = stack + stackSize - 16 */

        movl    4+4(%esp),%eax
        movl    %eax,0(%ebx)    /* func */
        movl    8+4(%esp),%eax
        movl    %eax,4(%ebx)    /* data */

        /*
         * Call rfork() syscall.  This is arcane I'm afraid because we cannot
         * use a 'call' due to possible stack reuse by one or the other 
         * process.  Instead we do a direct syscall.
         *
         * %edx is returned non-zero to the child, zero to the parent.  %eax
         * is returned as the child pid to the parent, and garbage to the
         * child.
         */

        pushl   $48             /* RFPROC|RFMEM         */
        pushl   $0              /* dummy                */
        movl    $0xfb,%eax      /* rfork syscall number */
        int     $0x80           /* syscall              */
        cmpl    $0,%edx         /* edx returns 0 to parent, pid in %eax */
        jne     child

        /*
         * parent returns pid of child in %eax ( returned by syscall ), and
         * controls original stack.
         */
        addl    $8,%esp         /* scrap syscall args   */
        popl    %ebx            /* pop saved registers  */
        ret                     /* return to parent     */

        /*
         * child must run function in new stack frame without messing with
         * old stack frame ( which might already be popped and reused by the
         * parent )
         */
child:
        movl    %ebx,%esp       /* switch stacks        */
        movl    %esp,%ebp       /* setup new frame pointer */
        popl    %eax            /* pop function address */
        call    *%eax           /* call function        */
        jmp     __exit          /* system exit on return*/



To Unsubscribe: send mail to majord...@freebsd.org
with "unsubscribe freebsd-current" in the body of the message

Reply via email to