You don't usually care about the fact that the shell creates processes—this is a basic assumption about the shell. In some application designs, you'll certainly be relying on shell scripts (batches of commands in a file) to do the work for you, but in other cases you'll want to create the processes yourself.
For example, in a large multiprocess system, you may want to have one master program start all the other processes for your application based on some kind of configuration file. Another example would include starting up processes when certain operating conditions (events) have been detected.
Let's take a look at the functions that QNX Neutrino provides for starting up other processes (or transforming into a different program):
Which function you use depends on two requirements: portability and functionality. As usual, there's a trade-off between the two.
The common thing that happens in all the calls that create a new process is the following. A thread in the original process calls one of the above functions. Eventually, the function gets the process manager to create an address space for a new process. Then, the kernel starts a thread in the new process. This thread executes a few instructions, and calls main(). (In the case of fork(), of course, the new thread begins execution in the new process by returning from the fork(); we'll see how to deal with this shortly.)