The QNX Persistent Publish/Subscribe (PPS) service is a small, extensible publish/subscribe service that offers persistence across reboots. It's designed to provide a simple and easy-to-use solution for both publish/subscribe and persistence in embedded systems, answering a need for building loosely connected systems using asynchronous publications and notifications.
With PPS, publishing is asynchronous: the subscriber need not be waiting for the publisher. In fact, the publisher and subscriber rarely know each other; their only connection is an object which has a meaning and purpose for both publisher and subscriber.
The PPS design is in many ways similar to many process control systems where the objects are control values updated by hardware or software. Subscribers can be alarm handling code, displays, and so on. Since there is a single instance of an object, persistence is a natural property that can be applied to it.
PPS maintains its objects in memory while it's running. It will, as required:
The underlying persistent storage used by PPS relies on a reliable filesystem, such as:
When PPS starts up, it immediately builds the directory hierarchy from the encoded filenames on the persistent filesystem. It defers loading the objects in the directories until first access to one of the files. This access could be an open() call on a PPS object, or a readdir() call on the PPS directory.
On shutdown, PPS always saves any modified objects to a persistent filesystem. You can also force PPS to save an object at any time by calling fsync() on the object. When PPS saves to a persistent filesystem, it saves all objects to a single directory.
|You can set PPS object and attribute qualifiers to have PPS not save specific objects or attributes.|
PPS uses an object-based system; that is, a system with objects whose properties a publisher can modify. Clients that subscribe to an object receive updates when that object changes — when the publisher has modified it.
PPS objects exist as files with attributes in a special PPS filesystem. By default, PPS objects appear under /fs/pps.
PPS directories can include special objects, such as .all and .notify, which applications can open to facilitate subscription behavior.
When PPS creates, deletes or truncates an object (a file or a directory), it places a notification string into the queue of any subscriber or publisher that has open either that object or the .all special object for the directory with the modified object. This file can be open in either full or delta mode.
PPS supports pathname open options, and objects and attribute qualifiers. PPS uses pathname open options to apply open options on the file descriptor used to open an object. Object and attribute qualifiers set specific actions to take with an object or attribute; for example, make an object non-persistent, or delete an attribute.
PPS objects support an extended syntax on the pathnames used to open them. Open options are added as suffixes to the pathname, following a question mark (“?”). That is, the PPS service uses any data that follows a question mark in a pathname to apply open options on the file descriptor used to access the object. Multiple options are separated by question marks.
You can set qualifiers to read() and write() calls by starting a line containing an object or attribute name with an open square bracket, followed by a list of single-letter or single-numeral qualifiers and terminated by a close square bracket.
To publish to a PPS object, a publisher simply calls open() for the object file with O_WRONLY to publish only, or O_RDWR to publish and subscribe. The publisher can then call write() to modify the object's attributes. This operation is non-blocking.
PPS supports multiple publishers that publish to the same PPS object. This capability is required because different publishers may have access to data which applies to different attributes for the same object.
In a multimedia system, for instance, io-media may be the source of a time::value attribute, while the HMI may be the source of a duration::value attribute. A publisher that changes only the time attribute will update only that attribute when it writes to the object. It will leave the other attributes unchanged.
PPS clients can subscribe to multiple objects, and PPS objects can have multiple subscribers. When a publisher changes an object, all clients subscribed to that object are informed of the change.
To subscribe to an object, a client simply calls open() for the object with O_RDONLY to subscribe only, or O_RDWR to publish and subscribe. The subscriber can then query the object with a read() call.
A subscriber can open an object in full mode, in delta mode, or in full and delta modes at the same time. The figure below illustrates the different information sent to subscribers who open a PPS object in full mode and in delta mode.
|In all cases, PPS maintains persistent objects with states — there is always an object. The mode used to open an object does not change the object; it determines only the subscriber's view of the object.|
In full mode (the default), the subscriber always receives a single, consistent version of the entire object as it exists at the moment when it is requested.
If a publisher changes an object several times before a subscriber asks for it, the subscriber receives the state of the object at the time of asking only. If the object changes again, the subscriber is notified again of the change. Thus, in full mode, the subscriber may miss multiple changes to an object — changes to the object that occur before the subscriber asks for it.
In delta mode, a subscriber receives only the changes (but all the changes) to an object's attributes. On the first read, since a subscriber knows nothing about the state of an object, PPS assumes everything has changed. Therefore, a subscriber's first read in delta mode returns all attributes for an object, while subsequent reads return only the changes since that subscriber's previous read. Thus, in delta mode, the subscriber always receives all changes to an object.
PPS uses directories as a natural grouping mechanism to simplify and make more efficient the task of subscribing to multiple objects. Subscribers can open multiple objects, either by calling open() then select() on the objects, or, more easily, by opening the special .all object which merges all objects in its directory.
PPS provides a mechanism to associate a set of file descriptors with a notification group. This mechanism allows you to read only the PPS special notification object to receive notification of changes to all objects associated with a notification group.