
Литература_1 / sys_arch
.pdf♥ 2008, QNX Software Systems GmbH & Co. KG. |
Bound multiprocessing (BMP) |
On a non-SMP system, there’s no need for a spinlock.
For more information, see the Multicore Processing User’s Guide.
Bound multiprocessing (BMP)
Bound multiprocessing provides the scheduling control of an asymmetric multiprocessing model, while preserving the hardware abstraction and management of symmetric multiprocessing. BMP is similar to SMP, but you can specify which processors a thread can run on. You can use both SMP and BMP on the same system, allowing some threads to migrate from one processor to another, while other threads are restricted to one or more processors.
As with SMP, a single copy of the OS maintains an overall view of all system resources, allowing them to be dynamically allocated and shared among applications. But, during application initialization, a setting determined by the system designer forces all of an application’s threads to execute only on a specified CPU.
Compared to full, floating SMP operation, this approach offers several advantages:
•It eliminates the cache thrashing that can reduce performance in an SMP system by allowing applications that share the same data set to run exclusively on the same CPU.
•It offers simpler application debugging than SMP since all execution threads within an application run on a single CPU.
•It helps legacy applications that use poor techniques for synchronizing shared data to run correctly, again by letting them run on a single CPU.
With BMP, an application locked to one CPU can’t use other CPUs, even if they’re idle. However, Neutrino lets you dynamically change the designated CPU, without having to checkpoint, and then stop and restart the application.
QNX Neutrino supports the concept of hard processor affinity through a runmask. Each bit that’s set in the runmask represents a processor that a thread can run on. By default, a thread’s runmask is set to all ones, allowing it to run on any processor. A value of 0x01 would allow a thread to execute only on the first processor.
By default, a process’s or thread’s children don’t inherit the runmask; there’s a separate inherit mask.
By careful use of these masks, a systems designer can further optimize the runtime performance of a system (e.g. by relegating nonrealtime processes to a specific processor). In general, however, this shouldn’t be necessary, because our realtime scheduler will always preempt a lower-priority thread immediately when a higher-priority thread becomes ready. Processor locking will likely affect only the efficiency of the cache, since threads can be prevented from migrating.
You can specify the runmask for a new thread or process by:
October 16, 2008 |
Chapter 5 • Multicore Processing 103 |
Choosing between AMP, SMP, and BMP |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
•setting the runmask member of the inheritance structure and specifying the
SPAWN_EXPLICIT_CPU flag when you call spawn()
Or:
•using the -C or -R option to the on utility when you launch a program. This also sets the process’s inherit mask to the same value.
You can change the runmask for an existing thread or process by:
•using the _NTO_TCTL_RUNMASK or _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT command to the ThreadCtl() kernel call
Or:
•using the -C or -R option to the slay utility. If you also use the -i option, slay sets the inherit mask to the same value.
For more information, see the Multicore Processing User’s Guide.
A viable migration strategy
As a midway point between AMP and SMP, BMP offers a viable migration strategy if you wish to move towards full SMP, but you’re concerned that your existing code may operate incorrectly in a truly concurrent execution model.
You can port legacy code to a multicore system and initially bind it to a single CPU to ensure correct operation. By judiciously binding applications (and possibly single threads) to specific CPUs, you can isolate potential concurrency issues down to the application and thread level. Resolving these issues will allow the application to run fully concurrently, thereby maximizing the performance gains provided by the multiple processors.
Choosing between AMP, SMP, and BMP
The choice between AMP, SMP, and BMP depends on the problem you’re trying to solve:
•AMP works well with legacy applications, but has limited scalability beyond two CPUs.
•SMP offers transparent resource management, but software that hasn’t been properly designed for concurrency might have problems.
•BMP offers many of the same benefits as SMP, but guarantees that uniprocessor applications will behave correctly, greatly simplifying the migration of legacy software.
As the following table illustrates, the flexibility to choose from any of these models lets you strike the optimal balance between performance, scalability, and ease of migration.
104 |
Chapter 5 • Multicore Processing |
October 16, 2008 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
Choosing between AMP, SMP, and BMP |
Feature |
SMP |
BMP |
AMP |
Seamless resource |
Yes |
Yes |
— |
sharing |
|
|
|
Scalable beyond dual |
Yes |
Yes |
Limited |
CPU |
|
|
|
Legacy application |
In most cases |
Yes |
Yes |
operation |
|
|
|
Mixed OS environment |
— |
— |
Yes |
(e.g. Neutrino and |
|
|
|
Linux) |
|
|
|
Dedicated processor by |
— |
Yes |
Yes |
function |
|
|
|
Intercore messaging |
Fast (OS primitives) |
Fast (OS primitives) |
Slower (application) |
Thread synchronization |
Yes |
Yes |
— |
between CPUs |
|
|
|
Load balancing |
Yes |
Yes |
— |
System-wide debugging |
Yes |
Yes |
— |
and optimization |
|
|
|
October 16, 2008 |
Chapter 5 • Multicore Processing 105 |

Chapter 6
Process Manager
In this chapter. . .
Introduction 109 |
|
Process management |
109 |
Memory management |
114 |
Pathname management |
119 |
October 16, 2008 |
Chapter 6 • Process Manager 107 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
Introduction |
Introduction
In QNX Neutrino, the microkernel is paired with the Process Manager in a single module (procnto). This module is required for all runtime systems.
The process manager is capable of creating multiple POSIX processes (each of which may contain multiple POSIX threads). Its main areas of responsibility include:
•process management — manages process creation, destruction, and process attributes such as user ID (uid) and group ID (gid).
•memory management — manages a range of memory-protection capabilities, shared libraries, and interprocess POSIX shared-memory primitives.
•pathname management — manages the pathname space into which resource managers may attach.
User processes can access microkernel functions directly via kernel calls and process manager functions by sending messages to procnto. Note that a user process sends a message by invoking the MsgSend*() kernel call.
It’s important to note that threads executing within procnto invoke the microkernel in exactly the same way as threads in other processes. The fact that the process manager code and the microkernel share the same process address space doesn’t imply a “special” or “private” interface. All threads in the system share the same consistent kernel interface and all perform a privilege switch when invoking the microkernel.
Process management
The first responsibility of procnto is to dynamically create new processes. These processes will 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, etc.
Process primitives
The process primitives include:
posix_spawn() |
POSIX |
spawn() |
QNX Neutrino |
fork() |
POSIX |
vfork() |
UNIX BSD extension |
exec*() |
POSIX |
October 16, 2008 |
Chapter 6 • Process Manager 109 |
Process management |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
posix_spawn()
The posix_spawn() function creates a child process by directly specifying an executable to load. To those familiar with UNIX systems, the 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:
•file descriptors
•process user and group IDs
•signal mask
•ignored signals
•adaptive partitioning (scheduler ) attributes
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.
spawn()
The QNX Neutrino spawn() function is similar to posix_spawn(). The spawn() function gives you control over the following:
•file descriptors
•process group ID
•signal mask
•ignored signals
•the node to create the process on
•scheduling policy
•scheduling parameters (priority)
110 |
Chapter 6 • Process Manager |
October 16, 2008 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
Process management |
•maximum stack size
•runmask (for SMP systems)
The basic forms of the spawn() function are:
spawn() Spawn with the explicitly specified path.
spawnp() Search the current PATH and invoke spawn() with the first matching executable.
There’s also a set of convenience functions that are built on top of spawn() and spawnp() as follows:
spawnl() |
Spawn with the command line provided as inline arguments. |
spawnle() |
spawnl() with explicitly passed environment variables. |
spawnlp() |
spawnp() that follows the command search path. |
spawnlpe() |
spawnlp() with explicitly passed environment variables. |
spawnv() |
Spawn with the command line pointed to by an array of pointers. |
spawnve() |
spawnv() with explicitly passed environment variables. |
spawnvp() |
spawnv() that follows the command search path. |
spawnvpe() |
spawnvp() with explicitly passed environment variables. |
When a process is spawn()’ed, the child process inherits the following attributes of its parent:
•process group ID (unless SPAWN_SETGROUP is set in inherit.flags)
•session membership
•real user ID and real group ID
•supplementary group IDs
•priority and scheduling policy
•current working directory and root directory
•file creation mask
•signal mask (unless SPAWN_SETSIGMASK is set in inherit.flags)
•signal actions specified as SIG_DFL
•signal actions specified as SIG_IGN (except the ones modified by inherit.sigdefault when SPAWN_SETSIGDEF is set in inherit.flags)
October 16, 2008 |
Chapter 6 • Process Manager 111 |
Process management |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
The child process has several differences from the parent process:
•Signals set to be caught by the parent process are set to the default action (SIG_DFL).
•The child process’s tms_utime, tms_stime, tms_cutime, and tms_cstime are tracked separately from the parent’s.
•The number of seconds left until a SIGALRM signal would be generated is set to zero for the child process.
•The set of pending signals for the child process is empty.
•File locks set by the parent aren’t inherited.
•Per-process timers created by the parent aren’t inherited.
•Memory locks and mappings set by the parent aren’t inherited.
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 in the QNX Neutrino Library
Reference.
fork()
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 lists some resources that are explicitly not inherited:
•process ID
•parent process ID
•file locks
•pending signals and alarms
•timers
The fork() function is typically used for one of two reasons:
•to create a new instance of the current execution environment
•to create a new process running a different program
112 |
Chapter 6 • Process Manager |
October 16, 2008 |