/* * vfork * * Description: vfork system call * * Parameters: void [no arguments] * * Retval: 0 (to child process) * !0 pid of child (to parent process) * -1 error (see "Returns:") * * Returns: EAGAIN Administrative limit reached * EINVAL vfork() called during vfork() * ENOMEM Failed to allocate new process * * Note: After a successful call to this function, the parent process * has its task, thread, and uthread lent to the child process, * and control is returned to the caller; if this function is * invoked as a system call, the return is to user space, and * is effectively running on the child process. * * Subsequent calls that operate on process state are permitted, * though discouraged, and will operate on the child process; any * operations on the task, thread, or uthread will result in * changes in the parent state, and, if inheritable, the child * state, when a task, thread, and uthread are realized for the * child process at execve() time, will also be effected. Given * this, it's recemmended that people use the posix_spawn() call * instead. * * BLOCK DIAGRAM OF VFORK * * Before: * * ,----------------. ,-------------. * | | task | | * | parent_thread | ------> | parent_task | * | | <.list. | | * `----------------' `-------------' * uthread | ^ bsd_info | ^ * v | vc_thread v | task * ,----------------. ,-------------. * | | | | * | parent_uthread | <.list. | parent_proc | <-- current_proc() * | | | | * `----------------' `-------------' * uu_proc | * v * NULL * * After: * * ,----------------. ,-------------. * | | task | | * ,----> | parent_thread | ------> | parent_task | * | | | <.list. | | * | `----------------' `-------------' * | uthread | ^ bsd_info | ^ * | v | vc_thread v | task * | ,----------------. ,-------------. * | | | | | * | | parent_uthread | <.list. | parent_proc | * | | | | | * | `----------------' `-------------' * | uu_proc | . list * | v v * | ,----------------. * `----- | | * p_vforkact | child_proc | <-- current_proc() * | | * `----------------' */ int vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) { thread_t child_thread; int err; if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_VFORK)) != 0) { retval[1] = 0; } else { /* * kludge: rely on uu_proc being set in the vfork case, * rather than returning the actual thread. We can remove * this when we remove the uu_proc/current_proc() kludge. */ proc_t child_proc = current_proc(); retval[0] = child_proc->p_pid; retval[1] = 1; /* flag child return for user space */ /* * Drop the signal lock on the child which was taken on our * behalf by forkproc()/cloneproc() to prevent signals being * received by the child in a partially constructed state. */ proc_signalend(child_proc, 0); proc_transend(child_proc, 0); /* flag the fork has occurred */ proc_knote(parent_proc, NOTE_FORK | child_proc->p_pid); DTRACE_PROC1(create, proc_t, child_proc); } return(err); }
/* * vfork * * Description: vfork system call * * Parameters: void [no arguments] * * Retval: 0 (to child process) * !0 pid of child (to parent process) * -1 error (see "Returns:") * * Returns: EAGAIN Administrative limit reached * EINVAL vfork() called during vfork() * ENOMEM Failed to allocate new process * * Note: After a successful call to this function, the parent process * has its task, thread, and uthread lent to the child process, * and control is returned to the caller; if this function is * invoked as a system call, the return is to user space, and * is effectively running on the child process. * * Subsequent calls that operate on process state are permitted, * though discouraged, and will operate on the child process; any * operations on the task, thread, or uthread will result in * changes in the parent state, and, if inheritable, the child * state, when a task, thread, and uthread are realized for the * child process at execve() time, will also be effected. Given * this, it's recemmended that people use the posix_spawn() call * instead. * * BLOCK DIAGRAM OF VFORK * * Before: * * ,----------------. ,-------------. * | | task | | * | parent_thread | ------> | parent_task | * | | <.list. | | * `----------------' `-------------' * uthread | ^ bsd_info | ^ * v | vc_thread v | task * ,----------------. ,-------------. * | | | | * | parent_uthread | <.list. | parent_proc | <-- current_proc() * | | | | * `----------------' `-------------' * uu_proc | * v * NULL * * After: * * ,----------------. ,-------------. * | | task | | * ,----> | parent_thread | ------> | parent_task | * | | | <.list. | | * | `----------------' `-------------' * | uthread | ^ bsd_info | ^ * | v | vc_thread v | task * | ,----------------. ,-------------. * | | | | | * | | parent_uthread | <.list. | parent_proc | * | | | | | * | `----------------' `-------------' * | uu_proc | . list * | v v * | ,----------------. * `----- | | * p_vforkact | child_proc | <-- current_proc() * | | * `----------------' */ int vfork(proc_t parent_proc, __unused struct vfork_args *uap, int32_t *retval) { thread_t child_thread; int err; if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_VFORK, NULL)) != 0) { retval[1] = 0; } else { uthread_t ut = get_bsdthread_info(current_thread()); proc_t child_proc = ut->uu_proc; retval[0] = child_proc->p_pid; retval[1] = 1; /* flag child return for user space */ /* * Drop the signal lock on the child which was taken on our * behalf by forkproc()/cloneproc() to prevent signals being * received by the child in a partially constructed state. */ proc_signalend(child_proc, 0); proc_transend(child_proc, 0); proc_knote(parent_proc, NOTE_FORK | child_proc->p_pid); DTRACE_PROC1(create, proc_t, child_proc); ut->uu_flag &= ~UT_VFORKING; } return (err); }
/* * fork * * Description: fork system call. * * Parameters: parent Parent process to fork * uap (void) [unused] * retval Return value * * Returns: 0 Success * EAGAIN Resource unavailable, try again * * Notes: Attempts to create a new child process which inherits state * from the parent process. If successful, the call returns * having created an initially suspended child process with an * extra Mach task and thread reference, for which the thread * is initially suspended. Until we resume the child process, * it is not yet running. * * The return information to the child is contained in the * thread state structure of the new child, and does not * become visible to the child through a normal return process, * since it never made the call into the kernel itself in the * first place. * * After resuming the thread, this function returns directly to * the parent process which invoked the fork() system call. * * Important: The child thread_resume occurs before the parent returns; * depending on scheduling latency, this means that it is not * deterministic as to whether the parent or child is scheduled * to run first. It is entirely possible that the child could * run to completion prior to the parent running. */ int fork(proc_t parent_proc, __unused struct fork_args *uap, int32_t *retval) { thread_t child_thread; int err; retval[1] = 0; /* flag parent return for user space */ if ((err = fork1(parent_proc, &child_thread, PROC_CREATE_FORK, NULL)) == 0) { task_t child_task; proc_t child_proc; /* Return to the parent */ child_proc = (proc_t)get_bsdthreadtask_info(child_thread); retval[0] = child_proc->p_pid; /* * Drop the signal lock on the child which was taken on our * behalf by forkproc()/cloneproc() to prevent signals being * received by the child in a partially constructed state. */ proc_signalend(child_proc, 0); proc_transend(child_proc, 0); /* flag the fork has occurred */ proc_knote(parent_proc, NOTE_FORK | child_proc->p_pid); DTRACE_PROC1(create, proc_t, child_proc); #if CONFIG_DTRACE if ((dtrace_proc_waitfor_hook = dtrace_proc_waitfor_exec_ptr) != NULL) (*dtrace_proc_waitfor_hook)(child_proc); #endif /* "Return" to the child */ proc_clear_return_wait(child_proc, child_thread); /* drop the extra references we got during the creation */ if ((child_task = (task_t)get_threadtask(child_thread)) != NULL) { task_deallocate(child_task); } thread_deallocate(child_thread); } return(err); }