Inter-Process communication

QNX provides the POSIX standard process/thread synchronization and communication services. The following tables show a resume of all these mechanisms:

Table 11-2. Synchronization Services

Sync MechanismImplemented inUsed byPOSIX
SemaphoreKernelProcess/ThreadsYES
MutexesKernelThreadsYES
Recursive MutexesKernelThreadsYES
Condition variableExternal ProcessThreadsYES
Readers/Writers locksExternal ProcessThreadsYES
BarriersExternal ProcessThreadsYES
FIFO schedulingKernelProcess/ThreadsNO
Atomic operationsKernelProcess/ThreadsNO

Table 11-3. Communication Services

Communication MechanismImplemented inUsed byPOSIX
Message PassingkernelProcessesNO
Signals--Processes/ThreadsYES
PIPESexternal processProcesses/ThreadsYES
FIFOsexternal processProcesses/ThreadsYES
Message Queuesexternal processThreadsYES
Shared Memoryprocess managerProcesses/ThreadsYES

Semaphores:

QNX provides the full-POSIX semaphores calls. Therefore, both named and unamed semaphores are provided.

The following table list the primitives available on QNX for managing the semaphores, as well as, the microkernel calls used by these routines:

Mutexes:

QNX provides the full POSIX mutexes calls where each of them, are associated with a microkernel call. The following table shows these routines:

The following mutex types are supported by QNX: PTHREAD_MUTEX_NORMAL,PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_RECURSIVE and PTHREAD_MUTEX_DEFAULT. Note that QNX supports the full POSIX mutex types.

Currently, there is only one POSIX mutex protocol available in QNX: the PTHREAD_PRIO_INHERIT protocol. Therefore, PTHREAD_PRIO_PROTECT protocol is not supported.

Finally, it is important to note how the classic priority inversion problem is solved: If a thread with a higher priority than the mutex owner tries to lock a mutex, then the effective priority of the current owner will be increased to that of the higher priority blocked thread waiting for mutex. The owner will returns to its real priority when it unlocks the mutex. This scheme is called priority inheritance.

Recursive mutexes

This service consists of that the attribute of the mutex can be modified (using PTHREAD_MUTEX_RECURSIVE type) to allow a mutex to be recursively locked by the same thread. So, QNX provides this POSIX functioning.

Priority inversion control:

As described earlier, the priority inversion control problem is solved using inmediate priority inheritance.

Condition variable:

In the same way as semaphores and mutexes, QNX provides full POSIX condition variable functions as well as the microkernel calls that are used by these routines. These functions are: pthread_cond_init(), pthread_cond_destroy(), pthread_cond_wait(), pthread_cond_signal() and pthread_cond_broadcast().

Reader/Writer Locks:

QNX provides full POSIX reader/writers functions. Now these functions are listed: pthread_rwlock_rdlock(), pthread_rwlock_wrlock(), pthread_rwlock_unlock(), pthread_rwlock_tryrdlock(), pthread_rwlock_trywrlock().

It is important to note that Reader/writer locks are not implemented directly within the kernel and have not associated a microkernel call either, but are built from the mutex and condvar services provided by the kernel.

Barriers:

QNX provides full POSIX barriers functions. Those functions are: pthread_barrier_init(), pthread_barrier_wait(), pthread_barrier_destroy() and pthread_barrierattr_* family.

FIFO scheduling:

This type of synchronization consist of selecting the POSIX FIFO scheduling algorithm. In this way, we can guarantee that two threads of the same priority don't execute the critical section concurrently.

Note that this feature is not accomplished on SMP systems.

Atomic Operations:

This synchronization mechanism allows to perform a short operation with the guarantee that the operation will performing atomically. The most important atomic operations that Neutrino provides are:

Message Passing:

QNX provides the message passing mechanism as the main form of IPC in QNX and Neutrino. Although other forms are available, those options are built over its native IPC.

The following illustration shows the state changes in a send-receive transaction.

As shown in figure, a thread that does a MsgSend() to another thread or process will be blocked until the receiving thread does a MsgReceive(), processes the message, and executes a MsgReply(). If a thread executes a MsgReceive() without a previously sent message pending, it will block until another thread executes a MsgSend(). Therefore, while the send and receive operations are blocked and synchronous, MsgReply() and MsgError() don't block and furthermore, unlock the client from MsgSend().

The following table lists the message passing API provided by QNX:

Moreover, QNX provides other message passing functions and mechanism that are based on the API defined above. This mechanism are: Multipart Transfers, Channels and Pulses.

Signals:

QNX support POSIX signals, realtime signals and traditional UNIX signals. Moreover, Neutrino provides eight special signals. Therefore, a total of 64 signals have been defined. The following table resumes these signals range:

PIPEs:

To uses pipes in QNX, the pipe resource manager, called pipe must be loaded.

This POSIX comunication mechanism are available on QNX using the symbol | to create a pipe from shell and the pipe() or popen() functions to create a pipe from programs.

FIFOs:

As well as pipes, for using FIFOs in QNX, the resource manager pipe must be loaded.

To manage FIFOs, QNX provides the following utilities: the mkfifo and rm command to create/delete FIFOs from shell, and the mkfifo(), remove() and unlink() functions to create/delete FIFOs from programs.

Message Queues:

QNX provides a full POSIX message queue API. To use this synchronous mechanism in Neutrino, the message queue resouce manager called mqueue must be loaded.

There is a fundamental different between QNX messages and POSIX messages queue: while QNX messages block and copy data directly from the address spaces of the process sending to the address space of process receiving, POSIX message queue, on the other hand, do not block and may have pending messages queued.

In Neutrino microkernel, all mesages queues created will appear in the filename space under the directory /dev/mqueue.

Shared Memory:

Full POSIX shared memory is implemented in Neutrino via the process manager called procnto. The following functions are implemented as messages to procnto: shm_open(), close(), mmap(), mummap(), mprotect(), msync(), shm_ctl() and shm_unlink().