Michael Schnell wrote:
> >Also, code which depends on vfork behaviour isn't portable except
> >for a few things which are allowed.
> >  
> What do you mean by "portable" ? Is the vfork() behavior not well 
> defined across different archs ? Or do you mean not portable to archs 
> that do have fork(). If so, why not continue to use vfork() ? Or do 
> these archs just map vfork() to fork() and thus ignore the 
> vfork-specialties (e.g. that the parent is blocked until the child does 
> *exec() or *exit() ) ?

If you're sticking just with uClinux, it's quite predictable.  If your
code might encounter Linux in its more general usages (LD_PRELOAD,
etc.) than you should be careful.  If you're considering portability on
other OSes, you have to be even more careful.

   - Some platforms map vfork -> fork.
     This includes old versions of Linux.
     But if your code is sensible, this doesn't matter.

   - Don't write to variables in the child process for the parent to read.
     This doesn't work if vfork is mapped to fork, obviously.

   - Calling open/close/dup/dup2 in a vfork'd child process (before execve)
     is normally fine.  But if your process is running with some
     LD_PRELOAD file tracing library which interposes those functions
     and the parent process is using threads, it can break.  There's
     quite a few such libraries.

   - Some LD_PRELOAD libraries map vfork -> fork for various reasons.
     One reason is to make file tracing work (previous item).  Another
     reason is to make some debuggers work.

   - Calling open/close/dup/dup2 also fails if your program is
     linked with some "userspace virtual filesystem" libraries.

   - On Linux and nearly all OSes, the child has separate file
     descriptors from the parent, so open/close/dup/dup2 are ok.  This
     is normal because vfork is nearly useless without it.  But on
     some OSes (possibly just old ones), the child and parent share
     file descriptors.

   - On Linux and nearly all OSes, you can set a signal handler to
     SIG_IGN or SIG_DFL in the child process which is useful before
     exec.  This isn't portable: on some other OSes, changing a signal
     handler affects the parent process too.

   - According to POSIX, the _only_ system functions a vfork child
     process may call safely are the exec family of functions and
     _exit.  But this is nearly useless in practice, and real
     systems are not so restrictive.

   - It is important that _exit is used in the child, not exit.
     (This is usually right with fork too).

   - The child code must not return from the function which calls
     vfork; must not call longjmp and similar functions; must not
     call malloc and similar functions; must not call printf and
     other stdio functions; must not even modify _local_ variables in
     the function which calls vfork (with specific exceptions), and
     must not call functions which affect global memory state (except
     intentionally).

   - Ironically, some implementations of exec functions which search a
     path call malloc, so may be unsafe.  So in portable code, only
     use execve or execv, and do path searching yourself if
     required, without calling malloc.

   - Optimising compilers place local variables on the stack in ways
     you might not expect, including using the same part of stack for
     different variables whose uses appear to not overlap.  This is
     why it's important for the child code not to write to most local
     variables - the compiler may optimise those stores to overwrite
     other parts of the stack.  With GCC and Linux this is not likely
     to be a problem because GCC knows to treat vfork specially, but
     on some other OSes you have to be more careful.  I avoid these
     issues for the most part by calling a separate function for the
     child actions prior to exec.

In my code, which I admit tries to be very portable to many OSes and
compilers, if I use vfork it's structured rather like the attached
snippet.

Enjoy :-)

-- Jamie


static int do_vfork(struct child_args *args)
     __attribute__((__noinline__));

static void child_actions(struct child_args *args)
     __attribute__((__noinline__, __noreturn__));

int
start_child_process(char *path, char **argv, char **envp);
{
    struct child_args args;
    memset(&args, 0, sizeof(args));
    args.path = path;
    args.argv = args;
    args.envp = envp;
    return do_vfork(&args);
};

static int
do_vfork(struct child_args *args)
{
    pid_t pid = vfork();
    if (pid == 0)
        child_actions(args);
    /* Parent stuff. */
    ...
}

static void
child_actions(struct child_args *args)
{
    /* Child stuff. */
    int error_code;
    ... dup2/close etc.
    .. setsid if needed, setpgrp if needed, fchdir if needed etc.
    .. sigaction/signal if needed.
    execve(args->path, args->argv, args->envp);
    if (args->error_pipe >= 0) {

        error_code = errno;
        write(args->error_pipe, &error_code, sizeof(error_code));
    }
    _exit(1);
}
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to