Concurrency

Three possibilities can happen to the creator during process creation:

  1. The child process is created and runs concurrently with the parent. In this case, as soon as process creation is successful, the process manager replies to the parent, and the child is made READY. If it's the parent's turn to run, then the first thing it does is return from the process-creation function. This may not be the case if the child process was created at a higher priority than the parent (in which case the child will run before the parent gets to run again).

    This is how fork(), forkpty(), popen(), and spawn() work. This is also how the spawn*() family of functions work when you specify a mode of P_NOWAIT or P_NOWAITO.

  2. The child replaces the parent. In fact, they're not really parent and child, because the image of the given process simply replaces that of the caller. Many things will change, but those things that uniquely identify a process (such as the process ID) will remain the same. This is typically referred to as "execing," since usually the exec*() functions are used.

    Many things will remain the same (including the process ID, parent process ID, and file descriptors) with the exception of file descriptors that had the FD_CLOEXEC flag set using fcntl(). See the exec*() functions for more on what will and will not be the same across the exec.

    The login command serves as a good example of execing. Once the login is successful, the login command execs into a shell.

    Functions you can use for this type of process creation are the exec*() and spawn*() families of functions, with mode passed as P_OVERLAY.

  3. The calling thread in the parent waits until the child terminates. You can make this happen by passing the mode as P_WAIT when you call one of the spawn*() functions.

    Note that what is going on underneath the covers in this case is that spawn() is called as in the first possibility above. Then, after it returns, waitpid() is called in order to wait for the child to terminate. This means that you can use any of the functions mentioned in our first possibility above to achieve the same thing if you follow them by a call to one of the wait*() functions (e.g., wait() or waitpid()).

Note: Many programmers coming from the Unix world are familiar with the technique of using a call to fork() followed by a call to one of the exec*() functions in order to create a process that's different from the caller. In QNX Neutrino, you can usually achieve the same thing more efficiently with a single call to one of the posix_spawn*() or spawn*() functions.