QNX的PPS功能-发布和订阅PPS

两盒软妹~` 提交于 2019-11-30 10:58:06


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.



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.


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:



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

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

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!