The process manager works closely with the Kernel, although it shares the same address space as the Kernel, and it is the only process to do so, the Process Manager runs as a true process. It is scheduled to run by the Kernel like all other processes and it uses the Kernel's message passing primitives to communicate with other processes.
The Process Manager is responsible for creating new processes in the system and managing the most fundamental resources associated with a process. These services are all provided via messages. You can easily create a process on another node by sending the process creation message to the Process Manager on that node.
QNX supports three process creation primitives:
The spawn() is unique to QNX while fork() and exec() are defined by POSIX.
When a process is created by one of the three primitives described above, it inherits much of its environment from its parent:
Item inherited | fork() | exec() | spawn() |
process ID | no | yes | no |
open files | yes | optional | optional |
file locks | no | yes | no |
pending signals | no | yes | no |
signal mask | yes | optional | optional |
ignored signals | yes | optional | optional |
signal handlers | yes | no | no |
environment variables | yes | optional | optional |
session ID | yes | yes | optional |
process group | yes | yes | optional |
real UID, GID | yes | yes | yes |
effective UID, GID | yes | optional | optional |
current working directory | yes | optional | optional |
file creation mask | yes | yes | yes |
priority | yes | optional | optional |
scheduler policy | yes | optional | optional |
virtual circuits | no | no | no |
symbolic names | no | no | no |
real-time timers | no | no | no |
The loading of process images is done by a loader thread. The loader code resides in the Process Manager, but the thread runs under the process ID of the new process. Once a program code has been loaded, the process is ready for execution. Note that all processes run concurrently with their parents. In addition, the death of a parent process does not automatically cause the death of its child process.
If the parent process has not issued a wait() or waitpid() call, the child process becomes a "zombie" process and will not terminate until the parent process issues a wait() or terminates. If you do not want a process to wait on the death of children, you should either set the _SPAWN_NOZOMBIE flag with qnx_spawn() or qnx_spawn_options() or set the action for SIGCHLD to be SIG_IGN via signal(). This way, children will not become zombies when they die.
A parent process can wait on the death of a child spawned on a remote node. If the parent of a zombie process terminates, the zombie is released.
The transactions are follows:
Splitting up applications into cooperating processes requires special considerations, however. If cooperating processes are to communicate reliably with each other, they must be able to determine each other's process ID. For example, let's say you have a database server process that provides services to an arbitrary number of clients. The clients start and stop at any time, but the server always remains available. How do client processes discover the process ID of the database server so they can send messages to it?
In QNX, the solution is to let processes give themselves a symbolic name. In the context of a single node, processes can register this name with the Process Manager on the node where they are running. Other processes can then ask the Process Manager for the process ID associated with that name.
The situation becomes more complex in a network environment where a server may need to service clients from several nodes across a network. QNX accordingly provides the ability to support both global names and local names. Global names are known accross the network, whereas local names are known only on the node where they are registered. Global names start with a slash(/). In order for global names to be used, a process known as a process name locator (i.e. nameloc utility) must be running on at least one node of a network. This process maintains a record of all global names that have been registered.
Up to ten process name locators may be active on a network at a particular time. Each maintains an identical copy of all active global names. This redundancy ensures that a network can continue to function properly even if one or more nodes supporting process name location fail simultaneously.
To attach a name, a server process uses the qnx_name_attach() C function. To locate a process by name, a client process uses the qnx_name_locate() C function.
Shell scripts and processes can pause for a specific number of seconds. For shell scripts this facility is provided by the sleep utility; for processes, it is provided by the sleep() C function. The delay() function takes a time interval specified in milliseconds.
A process can create one or more timers using timer_create() C function. This function lets specify the type of event-reporting mechanism to be used:
You can also have a timer go off repeatedly at a specified interval. For example let's say you have a timer armed to go off at 9 am tomorrow morning. You can specify that it should also go off every five minutes thereafter.
To arm timers with an absolute or relative time interval, use timer_settime() C function.
To remove timers, you can use the timer_delete() C function. A timer can remove itsef when its time interval expires, provided these conditions are met: