The first responsibility of procnto is to dynamically create new processes. These processes then depend on procnto's other responsibilities of memory management and pathname management. Process management consists of both process creation and destruction as well as the management of process attributes such as process IDs, process groups, user IDs, and so on.
The process primitives include:
The posix_spawn() function creates a child process by directly specifying an executable to load. To those familiar with UNIX systems, the posix_spawn() call is modeled after a fork() followed by an exec*() . However, it operates much more efficiently in that there's no need to duplicate address spaces as in a fork(), only to destroy and replace it when the exec*() is called.
In a UNIX system, one of the main advantages of using the fork()-then-exec*() method of creating a child process is the flexibility in changing the default environment inherited by the new child process. This is done in the forked child just before the exec*(). For example, the following simple shell command would close and reopen the standard output before exec*()'ing:
ls > file
You can do the same with posix_spawn(); it gives you control over the following classes of environment inheritance, which are often adjusted when creating a new child process:
There's also a companion function, posix_spawnp() , that doesn't require the absolute path to the program to spawn, but instead searches for the executable using the caller's PATH.
Using the posix_spawn() functions is the preferred way to create a new child process.
The BlackBerry 10 OS spawn() function is similar to posix_spawn(). The spawn() function gives you control over the following:
The basic forms of the spawn() function are:
There's also a set of convenience functions that are built on top of spawn() and spawnp() as follows:
When a process is spawn()'ed, the child process inherits the following attributes of its parent:
The child process has several differences from the parent process:
If the child process is spawned on a remote node, the process group ID and the session membership aren't set; the child process is put into a new session and a new process group.
The child process can access the parent process's environment by using the environ global variable (found in <unistd.h>).
For more information, see the spawn() function.
The vfork() function (which should be called only from a single-threaded process) is useful when the purpose of fork() would have been to create a new system context for a call to one of the exec*() functions. The vfork() function differs from fork() in that the child doesn't get a copy of the calling process's data. Instead, it borrows the calling process's memory and thread of control until a call to one of the exec*() functions is made. The calling process is suspended while the child is using its resources.
The vfork() child can't return from the procedure that called vfork(), since the eventual return from the parent vfork() would then return to a stack frame that no longer existed.
The fork() function creates a new child process by sharing the same code as the calling process and duplicating the calling process's data to give the child process an exact copy. Most process resources are inherited. The following resources are explicitly not inherited:
The fork() function is typically used for one of two reasons:
When creating a new thread, common data is placed in an explicitly created shared memory region. Prior to the POSIX thread standard, this was the only way to accomplish this. With POSIX threads, this use of fork() is better accomplished by creating threads within a single process using pthread_create() .
When creating a new process running a different program, the call to fork() is soon followed by a call to one of the exec*() functions. This too is better accomplished by a single call to the posix_spawn() function or the BlackBerry 10 OS spawn() function, which combine both operations with far greater efficiency.
Since BlackBerry 10 OS provides better POSIX solutions than using fork(), its use is probably best suited for porting existing code and for writing portable code that must run on a UNIX system that doesn't support the POSIX pthread_create() or posix_spawn() API.
The exec*() family of functions replaces the current process with a new process, loaded from an executable file. Since the calling process is replaced, there can be no successful return. The following exec*() functions are defined:
The exec*() functions usually follow a fork() or vfork() to load a new child process. This is better achieved by using the posix_spawn() call.
Processes loaded from a file system using the exec*(), posix_spawn() or spawn() calls are in ELF format. If the file system is on a block-oriented device, the code and data are loaded into main memory. By default, the memory pages containing the binaries are demand-loaded, but you can use the procnto -m option to change this; for more information, see Locking memory.
If the file system is memory mapped (for example, ROM/flash image), the code needn't be loaded into RAM, but may be executed in place. This approach makes all RAM available for data and stack, leaving the code in ROM or flash. In all cases, if the same process is loaded more than once, its code is shared.