Why Use POSIX Spawn?
The posix_spawn() and posix_spawnp() functions are both used to create a new child process. The child process then executes a file. These functions were specified by POSIX to standardize the method of creating new processes for machines that do not have fork system call support. These machines are usually small and lack the embedded systems for lMMU support.
The two functions combine fork and exec, with some additional steps that will execute the child. They act as a subset of functionalities, usually achieved with a fork, for all the system calls and embedded systems that lack such functionality.
Example 1: posix_spawn()
In this example, we will use the spawn () function to create and execute a new child process. Then, we will explain all the relevant arguments used in the function.
The arguments used in the example are as follows:
|<spawn.h>||Used to define all spawn performing operations.|
|path||The name of the path that is to be executed.|
|fd_count||The number of the entries with the array of fd_map. If fd_count is equal to 0, then the fd_map is ignored. In such cases, the child process inherits all the file descriptors, ignoring the ones that were modified.|
||An array of file descriptors to be inherited by the child process. Here, if the value of fd_count is not 0, then fd_map is needed to bring the fd_count file descriptors up to a supreme value of OPEN_MAX. It has:
· The child process input
· The output
· The error values
|inherit||The struct inheritance shows that users want their child process to inherit everything from the parent.|
|argv||The pointer to a particular argument vector. The argv value cannot be NULL and must be the filename that is being loaded. The argv value cannot be equal to NULL.|
|envp||Points to an array of character pointers. Each of the pointers in this array points to an environment variable. The finish point of the array is a NULL pointer.|
Example 2: test.c
In the following example, a new child process is created to run the command by /bin/sh -c. This is the value passed as the first argument. The test.c code is as follows:
In the above example, we called the libraries, then called the spawn.h header. You will also see the posix_spawn() called to create a child process in the above example. The spawn and spawnp functions are used in place of the fork and exec functions. Spawn() has flexibility and provides a lot of ease to the users in many ways. It is a bit dissimilar from system() and exec(). It will return and create the fresh child process. In our example, it is pid. Above, you can see that the wait function waitpid(), then system() is used. Notice that the spawn() and fork() calling processes are the same, and the method of implementation is more or less the same for both functions.
We will now execute the example using a gcc compiler. You can also use any other compiler of your choice:
Next, run the following:
The output of the above command will look as follows:
The child pid will be created, as you can see in the above output.
Libc: Use the -l c to link the gcc compiler. Here, notice that this library is included automatically.
The spawn() function is based on POSIX 1003.1d draft standard used as posix_spawn(). The C library includes spawn*() functions. Here, we will list a few suffixes, along with their descriptions:
e: used as an array for environment variables.
l: used as a NULL-terminated list of the arguments used inside the program.
p: used to define a relative path. If the path does not have a slash in its value, then the system uses and searches the PATH environment variable for any similar program.
v: acts as a vector of arguments inside the program.
Mapping File Descriptors
In spawn(), we practice the fd_count and fd_map arguments to call out the file descriptors. It specifies which child to inherit.
The number used as a file descriptor for the child process depends on its location inside the fd_map. Here, we will consider the example of the parent with file descriptors valued 1, 3, and 5, then the mapping will be something like this:
|For the child||For the parent|
Note that if you are using the explicit fd_map to match these file descriptors with the child and parent, then you must map the SPWAN_FDCLOSE function to proceed.
In Spawn, users need to call out any of the following flags in case of inheritance. Some examples of Spawn flags and their descriptions are given below:
|SPAWN_ALIGN_DEFAULT||This flag is used to set up the default settings of the setup for alignment.|
|SPAWN_ALIGN_FAULT||This flag is used for fault misalignment of the data references.|
|SPAWN_ALIGN_NOFAULT||This flag is used to fix the fault misalignment.|
|SPAWN_DEBUG||This flag is used to debug the kernel.|
|SPAWN_EXEC||SPAWN acts like exec*() using this flag.|
|SPAWN_EXPLICIT_CPU||This flag is used to set the run mask and inherit the mask=run mask member.|
|SPAWN_EXPLICIT_SCHED||This flag is used to set the scheduling policy.|
The <spawn.h> defines that mask SPAWN_ALIGN_MASK used to align the flags listed above.
|pid_t pgroup||The child process group if you specify the SPAWN_SETGROUP in the flag’s member.|
|int runmask||The runmask of the child process to inherit the masks that are agreed based on the value of this member.|
|sigset_t sigmask||The signal mask for the child process that is used to specify the status of flag members.|
|sigset_t sigdefault||The set of the child processes of the defaulted signals.|
The posix_spawn() and posix_spawnp() functions can also fail in some cases, such as the following:
EINVAL: This is the case when the value identified by file_actions or attrp is not correct and adequate.
When the underlying fork (2), fork (2), or clone (2) call fails, the spawn() functions will return an error number.
ENOSYS: This is the case if the function and its support is not included or provided within a system.
This tutorial covered the basic functionalities provided by POSIX_spawn() and the functions used by it to execute and perform its functions. We also covered the flags and errors commonly used by Spawn.