QNX的PPS功能-釋出和訂閱PPS

鋒影Q發表於2017-06-04


author 鋒影

e-mail:174176320@qq.com



為什麼會有這種機制?

簡單來說常用有兩種機制不滿足複雜系統的設計:

1)非同步機制,雖然速度比較快,因為其不用等待接收端返回資料(優點)。()但是對於複雜系統的訊息傳送模式,非同步訊息傳送是一個低端的解決方案——它將錯誤處理、端到端語義和緩衝管理等負擔推高至應用程式級。因此,架構師在設計採用非同步訊息傳送模式的系統時,必須開發一個或多個協議,以確保在所有應用中訊息傳送正確進行,因為它們必須保證這些應用程式在高負載條件下能夠獲得足夠的記憶體分配用於訊息緩衝。

    在簡單的系統中,完成這些任務可能不太費勁,但對於設計或升級複雜的系統,這些任務可能構成艱鉅的挑戰。進一步來說,它們給應用程式級或者應用開發流程帶來的複雜操作不僅會影響設計和開發程式,也會影響裝置的審批,進而影響裝置認證。

2)同步機制,必須等待反饋才能next。在實時系統中,系統框架會承受處理訊息傳送錯誤和訊息緩衝的負擔

在預設狀態下,QNX PPS服務是作為推送式釋出系統執行的;也就是說,釋出者會將資料推送到物件中,訂閱者會根據通知或在閒暇時讀取資料。(手機app更新訊息就是這樣)

但有些資料(如介面上的資料包計數)變化太快,因此無法通過使用預設推送釋出的PPS有效地進行釋出。為此,QNX PPS還提供了一種選項,允許訂閱者將PPS變成提取式發布系統。當訂閱者開啟具有該選項的物件併發出一個read()呼叫時,該物件的所有釋出者會收到一個通知以在物件中寫入當前資料。訂閱者的讀取會一直阻塞直至物件的資料得到更新並返回新的資料。利用這種提取機制,PPS訂閱者能按其需要的速度向釋出者檢索資料,從而實現了真正意義上的按需釋出。

利用PPS服務,釋出者與訂閱者之間並不認識;他們之間唯一的聯絡是具有某種含義和用途的物件。(靈活可擴充套件)


QNX PPS service

The QNX Persistent Publish/Subscribe (PPS) service is a small, extensible publish/subscribe service that offers persistence across reboots. It is 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.


執行pps:

pps [options]


引數:

-b

Do not run in the background. Useful for debugging.
-l argument
Set the object load behavior, as follows:
  • 0 — load directory names and objects on demand. Default.
  • 1 — load all directory and object names on startup, but do not load object contents. Load object contents on demand.
  • 2 — load directories, objects, and object contents on startup.
-m mount
Specify the mountpath for PPS. Default is /pps/
-p path
Set the path for backing up the persistent storage.
-t period
Specify the periodicity of the forced persistence, in milliseconds. For example-t 5000 forces the PPS service to write to persistent storage every five seconds. Default is no forced persistence.
-v
Enable verbose mode. Increase the number of “v”s to increase verbosity.



Publishing

To publish to a PPS object, a publisher simply calls open()for the object file withO_WRONLYto publish only, orO_RDWR to publish and subscribe. The publisher can then call write() to modify the object's attributes. This operation is non-blocking.

Creating, modifying and deleting

You can create, modify, and delete objects and attributes:

To create a new object:
Create a file with the name of the object. The new object will come into existence with no attributes. You can then write attributes to the object, as required.
To delete an object:
Delete the object file.
To create a new attribute:
Write the attribute to the object file.
To modify an attribute:
Write the new attribute value to the object file.
To delete all existing attributes:
Open the object with O_TRUNC.
To delete one attribute:
Prefix its name with a minus sign, then call write(). For example:
sprintf( ppsobj, "-url\n" ); // Delete the "url" attribute
write( ppsobj-fd, ppsobj, strlen( ppsobj ) );


Subscribing

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, orO_RDWR to publish and subscribe. The subscriber can then query the object with aread() call.

A read returns the length of the data read, in bytes. If the allocated read buffer is too small for the data being read in, the read fails.

Blocking and non-blocking reads

By default, reads to PPS objects are non-blocking; that is, PPS defaults a normalopen() toO_NONBLOCK, and reads made by the client that opened the object do not block. This behavior is atypical for most filesystems. It is implemented so that standard utilities will not hang waiting for a change when they make aread() call on a file.

For example, with the default behavior, you could tar up the entire state of PPS using the standard tar utility. Without this default behavior, however, tar would never make it past the first file opened and read.


Setting PPS to block

Though the PPS default is to open objects for non-blocking reads, the preferred method for querying PPS objects is to use blocking reads. With this method, a read waits until the object or its attributes change, then returns data.

To have reads block, you need to open the object with the ?wait pathname open option, appended as a suffix to the pathname for the object. For example:

  • to open the PlayList object for the default non-blocking reads, use the pathname:"/pps/media/PlayList"
  • to open the PlayList for blocking reads, use the pathname plus the option:"/pps/media/PlayList?wait"

For information about ?wait and other pathname open options, see the chapterOptions and Qualifiers.

A typical loop in a subscriber would live in its own thread. For a subscriber that used the opened the object with the?waitoption, this loop might do the following:

/* Assume that the object was opened with the ?wait option
   No error checking in this example. */
for(;;) {
    read(fd, buf, sizeof(buf)); // Read waits until the object changes.
    process(buf);
}

ClearingO_NONBLOCK

If you have opened an object without the ?wait option, and want to change to blocking reads, you can clear theO_NONBLOCKbit, so that the subscriber waits for changes to an object or its attributes.

To clear the bit you can use the fcntl() function. For example:

flags = fcntl(fd, F_GETFL);
flags &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

Or you can use the ioctl() function:

int i=0;
ioctl(fd,FIONBIO,&i);

After clearing the O_NONBLOCK bit, you can issue a read that waits until the object changes.

io_notify() functionality

The PPS service implements io_notify() functionality, allowing subscribers to request notification via aPULSE,SIGNAL,SEMAPHORE, etc. On notification of a change, a subscriber must issue aread() to the object file to get the contents of the object. For example:

/* Process events while there are some */
while(ionotify(fd, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT,
    &event) & _NOTIFY_CONT_INPUT) {
	if(read(fd, buf, sizeof(buf)) > 0) // Best to read with O_NONBLOCK
    process(buf);
    }
/* The event will be triggered in the future to get our attention */

Getting notifications of data on a file descriptor

You can use either one of two simple mechanisms to receive notifications that data is available on a file descriptor:

  • You can issue a blocking read() by either opening the object with the ?wait syntax on theopen() call, or by clearing theO_NONBLOCK flag using the fnctl() function after theopen() call.
  • You can use the QNX io_notify() mechanisms to receive a user-specified event; you can also use theselect() function, which usesio_notify() under the covers. Seeio_notify() functionality above.

Subscription Modes

A subscriber can open an object in full mode, in delta mode, or in full and delta modes at the same time. The default is full mode. To open an object in delta mode, you need to open the object with the?delta pathname open option, appended as a suffix to the pathname for the object.

For information about ?delta and other pathname open options, see the chapterOptions and Qualifiers.

Full mode

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 askingonly. 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.

Delta mode

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.

The figure below illustrates the different information sent to subscribers who open a PPS object in full mode and in delta mode.


PPS subscription modes


Comparison of PPS full and delta subscription modes.

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 only determines the subscriber's view of changes to the object.

Delta mode queues

When a subscriber opens an object in delta mode, the PPS service creates a new queue of object changes. That is, if multiple subscribers open an object in delta mode, each subscriber has its own queue of changes to the object, and the PPS service sends each subscriber its own copy of the changes. If no subscriber has an object open in delta mode, the PPS service does not maintain any queues of changes to that object.


Note: On shutdown, the PPS service saves its objects, but objects' delta queues are lost.

Changes to multiple attributes

If a publisher changes multiple attributes with a single write() call, then PPS keeps the deltas together and returns them in the same group on a subscriber'sread() call. In other words, PPS deltas maintain both time and atomicity of changes. For example:

write()                     write()
  time::1.23                 time::1.24
  duration::4.2              write()
                             duration::4.2

read()                     read()
  @objname                   @objname
  time::1.23                 time:1.24
  duration::4.2              @objname
                             duration::4.2

Server objects

When a client writes to a server object, only the application that created it with the?server option (called the "server") will get the message.Other clients cannot see that message.

At write-time, PPS appends a unique identifier to the object name so that the "server" knows which client connection is sending the message. This allows theconnection to have stateful information. For example:

@foo.1234

indicates object foo with client identifier 1234. When a client connects, the serverreads a new object that is prefixed with a +symbol (for example,+@foo.1234). Disconnects are sent to the "server" and the + prefixis changed to a - prefix.

When a server replies, it must write the object name with the unique identifier appended so that the response is sent only to the client that is identified by the unique identifier.If a server does not append the unique identifier to the object name, the message will be broadcast to all clients that are connectedto the object.

When an object is opened with the ?server option it automatically becomes a critical publisher of that object. It also automaticallyreceives notifications in delta mode.

The following figure shows a PPS transaction using the ?server option:


A PPS server transaction


Using the ?server option

Subscribing to multiple objects

PPS supports three special objects which facilitate subscribing to multiple objects:

  • .all — open to receive notification of changes to any object in this directory.
  • .notify — open to receive notification of changes to any object associated with a notification group.

Subscribe to all objects in a directory

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 callingopen() thenselect() on their file descriptors. More easily, they can open the special.all object, which merges all objects in its directory.

For example, assume the following object file structure under /pps:

rear/left/PlayCurrent
rear/left/Time
rear/left/PlayError

If you open rear/left/.all you will receive a notification when any object in therear/left directory changes. A read in full mode will return at most one object per read.

read()
@Time
  position::18
  duration::300

read()
@PlayCurrent
  artist::The Beatles
  genre::Pop
  ... the full set of attributes for the object

If you open a .all object in delta mode, however, you will receive a queue of every attribute that changes in any object in the directory. In this case, a singleread() call may include multiple objects.

read()
@Time
  position::18
@Time
  position::19
@PlayCurrent
  artist::The Beatles
  genre::Pop

Notification groups

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 any of the objects associated with that notification group.

Creating notification groups

To create a notification group:

  1. Open the .notify object in the root of the PPS file system.
  2. Read the .notify object; the first read of this file returns a short string (less than 16 characters) with the name of the group to which other file descriptors should associate themselves.

To associate a file descriptor to a group, on an open, specify the pathname open option?notify=group:value, where:

  • group is the string returned by the first read from the .notify file
  • value is any arbitrary string; a subscriber will use this string to determine which objects bound to the notification group have data available for reading

Note: The returned notification group string has a trailing linefeed character, which you must remove before using the string.

For information about ?notify and other pathname open options, see the chapterOptions and Qualifiers.

Using notification groups

Once you have created a notification group and associated file descriptors to it, you can use this group to learn about changes to any of the objects associated with it.

Whenever there is data available for reading on any of the group's file descriptors, reads to the notification object's file descriptor return the string passed in the?notify=group:value pathname option.

For example, with PPS is mounted at /pps, you could write something like the following:

char noid[16], buf[128];
int notify_fd, fd1, fd2;

notify_fd = open("/pps/.notify", O_RDONLY);
read(notify_fd, &noid[0], sizeof(noid));

sprintf(buf, "/pps/fish?notify=%s:water", noid);
fd1 = open(buf, O_RDONLY);
sprintf(buf, "/pps/dir/birds?notify=%s:air", noid);
fd2 = open(buf, O_RDONLY);

while(read(notify_fd, &buf, sizeof(buf) > 0) {
    printf("Notify %s\n", buf);
}

The data printed from the “while” loop in the example above would look something like the following:

Notify 243:water
Notify 243:water
Notify 243:air
Notify 243:water
Notify 243:air

Note: When reading from an object that is bound to a notification group, a subscriber should do multiple reads for each change indicated. There may be more than one change on an item, but there is no guarantee that every change will be indicated on the notification group's file descriptor.

Notification of closed file descriptors for objects

If a file descriptor for an object which is part of a notification group is closed, the string passed with the change notification is prefixed by a minus (“-”) sign. For example:

-243:air

相關文章