Pull to refresh

ОСРВ QNX: PPS — слабосвязанное межзадачное взаимодействие

*nix *
Очередное продолжение цикла заметок об операционной системе реального времени QNX. Сегодня я бы опять хотел рассказать о межзадачном взаимодействии, но только об одном определённом механизме — Persistent Publish/Subscribe (PPS, устойчивая служба публикации/подписки). Сразу хочу отметить, это новый вид взаимодействия в QNX Neutrino, который появился только в версии 6.5.0. Технология PPS реализована не в микроядре, и за работу этого механизма отвечает специальный менеджер с говорящим названием pps.

Служба PPS оказалась настолько надёжной, удобной и простой в использовании, что применяется в новых решениях и продуктах, таких как, например, Smart Energy, QNX Car и даже планшет BlackBerry PlayBook.

В этой заметке мы будем знакомиться с PPS на практике, будет рассказано об особенностях технологии, а в самом конце будет показано, что PPS это настоящий космополит среди механизмов межзадачного взаимодействия, и многие языки программирования поддерживают его «из коробки».

Для чего нужен PPS


Использование PPS позволяет упростить построение систем, состоящих из множества компонентов. В будущем эти компоненты можно будет изменять, дополнять и даже удалять без переделки всей системы в целом и без изменения других компонентов. Различные команды разработчиков, разрабатывающие приложения с использованием различных технологий и языков программирования, могут разрабатывать компоненты, которые будут взаимодействовать друг с другом без всякого знания друг о друге.

Основные преимущества PSS над другими способами межзадачного взаимодействия:

  • Слабое связывание — позволяет легко изменять набор взаимодействующих компонентов.
  • Большая гибкость — взаимодействие может строиться не только по классической схеме один-к-одному, но также и один-ко-многим и многие-к-одному.
  • Сохранение данных между перезагрузками — не надо заботиться о сохранении текущих данных, достаточно разместить их в объекте PPS и менеджер pps сделает всё за вас.
  • Независимость от языка — многие языки и платформы уже поддерживают PPS, а при необходимости эту поддержку можно добавить. Компоненты реализованные на разных языках программирования могут легко взаимодействовать друг с другом.

Скорее всего, если система уже сейчас состоит из трёх-четырёх компонентов, а в дальнейшем она может изменяться и расширяться, то применение PPS будет правильным шагом.

Что такое PPS


PPS это довольно просто. Это файлы в которые отображаются объекты со своими свойствами. Даже не отображаются, а прямо там и живут. В одном файле хранится только один объект. Любой процесс может читать и писать такой файл, если POSIX атрибуты доступа это позволяют. Работать с PPS можно из программы практически на любом языке, т.к. для доступа к объектам используются обычные функции read(), write() и т.п. Наверное, хватит занудства и, пожалуй, пора приступить к практическим занятиям.

Первым делом необходимо запустить менеджер pps:

# pps

У менеджера, конечно, есть аргументы командной строки, но ничего принципиального для нас там нет. Все желающие могут ознакомиться с описанием в справочном руководстве. При выполнении всех последующих упражнений будем считать, что менеджер pps уже запущен. Теперь попробуем создать какой-нибудь объект, например, figure:

# touch /fs/pps/figure

Это всё. Теперь объект можно прочитать:

# cat /fs/pps/figure
@figure

Так можно увидеть название объекта. Пока ещё у объекта нет свойств. Можно их добавить, например, form:

# echo "form::square" >> /fs/pps/figure
# cat /fs/pps/figure
@figure
form::square

Можно добавить ещё одно свойство, например, color:

# echo "color::red" >> /fs/pps/figure   
# cat /fs/pps/figure                  
@figure
color::red
form::square

Будьте осторожны, следующая команда удалит объект и создаст его заново с одним свойством:

# echo "color::green" > /fs/pps/figure      
# cat /fs/pps/figure                   
@figure
color::green

Надеюсь, что вы знаете разницу между > и >>. Любое свойство или несколько свойств объекта можно изменить или добавить в любой момент:

# echo "color::blue\nform::circle" >> /fs/pps/figure
# cat /fs/pps/figure                                
@figure
color::blue
form::circle

Любое свойство объекта можно удалить:

# echo "-color" >> /fs/pps/figure                                                                              
# cat /fs/pps/figure                                 
@figure
form::circle

Если же сам объект больше не нужен, то и его можно удалить:

# rm /fs/pps/figure
# cat /fs/pps/figure
/fs/pps/figure: No such file or directory

Думаю, что читатели Хабра достаточно грамотные люди, чтобы понять смысл примеров и переписать их на своём любимом языке.

А сейчас, мне кажется, пора объяснить смысл названия PPS — Persistent Publish/Subscribe.

Persistent (Устойчивая)


Служба PPS во время работы хранит данные в оперативной памяти. Но в тоже время, PPS гарантирует, что данные сохраняются между перезагрузками в энергонезависимом хранилище. Обычно данные сохраняются в файловой системе на диске или flash-памяти. При необходимости разработчик может организовать сохранение данных на нестандартных носителях.

При старте, менеджер pps восстанавливает данные из энергонезависимого хранилища. Можно указать различные режимы восстановления данных при помощи опции -l:

  • загружать имена объектов и директорий по запросу;
  • сразу загружать имена директорий и объектов, но содержимое объектов загружать по запросу;
  • загружать всё при запуске менеджера.

Publishing (Публикация)


Об этом мы уже достаточно знаем1. Необходимо просто открыть файл на запись и записывать и удалять атрибуты объекта:

sprintf( ppsobj, "-color\n" ); // Delete the "color" attribute
write( ppsobj-fd, ppsobj, strlen( ppsobj ) );

Пожалуй, стоит отметить, что несколько процессов могут одновременно открывать один и тот же объект на запись и изменять атрибуты. Это может быть полезно в том случае, если за разные атрибуты отвечают разные процессы.

Subscribing (Подписка)


О подписке нам тоже уже кое-что известно, но далеко не всё. Не думаете же вы, что PPS настолько прост и тривиален. Да, тут точно где-то порылась собака. И порылась она именно в подписке. Существуют тонкости подписки, такие как, например, блокирующее чтение и режим дельты. Рассмотрим некоторые из этих возможностей.

Блокирующее чтение

По умолчанию чтение из файла-объекта PPS является неблокирующим. Это обычное поведение для других файловых систем. Сделано это специально, чтобы стандартные утилиты вели себя как обычно. Самый простой способ (но не всегда самый удачный) получить изменения объекта своевременно, это блокирующее чтение. Чтобы открыть объект PPS в режиме блокирующего чтения достаточно открыть файл с указанием квалификатора ?wait.

Давайте проведём смелый эксперимент и в одной консоли будем менять свойства объекта, а в другой будем следить за этими изменениями. Для этого понадобится две консоли. Если запущена графическая среда Photon, то всё просто. Если же работа идёт в текстовой консоли, то переключение между ними осуществляется комбинациями клавиш Ctrl+Alt+n (здесь n это клавиша с цифрой 1, 2 и т.д., а не функциональная клавиша F1, F2 как в Linux, например). И так, создаём объект:

# echo "color::green" >> /fs/pps/figure

А во второй консоли открываем его в режиме блокирующего чтения:

# cat /fs/pps/figure?wait
@figure
color::green

Заметьте, что утилита cat не завершается, а блокируется по чтению. Теперь в первой консоли набираем пару команд:

# echo color::red >> /fs/pps/figure
# echo color::white >> /fs/pps/figure

Во второй консоли продолжается вывод:

@figure
color::red
@figure
color::white

Особенно наглядно всё это проделать в Photon, когда две консоли перед глазами. Завершить работу cat можно обычным Ctrl+C.

Переключать режим чтения можно налету. Следующий код на C, иллюстрирует эту возможность:

flags = fcntl( fd, F_GETFL );
flags |= O_NONBLOCK;
fcntl( fd, F_SETFL, flags );

Уведомление о новых данных

Существуют две возможности своевременно получать новые данные из PPS:

  • Открывать объект в режиме блокирующего чтения.
  • Использовать механизм уведомлений QNX io_notify() или основанную на нём POSIX функцию select().

Использование функции select() предпочтительнее, т.к. позволяет изменения свойств нескольких объектов.

Режимы подписки

Есть два режима подписки:

  • полный, когда при изменение объекта подписчик прочитает все свойства объекта; и
  • «дельта», когда читаются только изменившиеся с последнего чтения свойства объекта.

image
Рис. 1. Полный и «дельта» режимы подписки PPS.

Стоит отметить, что в полном режиме всегда читается объект целиком со всеми его свойствами, которые существуют на момент чтения объекта. А это значит, что если между чтениями одно и тоже свойство было изменено несколько раз, то прочитано будет последнее изменение.

В «дельта»-режиме наоборот, подписчик получает все изменения свойств объекта, т.е. ничего не будет пропущено2. Чтобы открыть объект в режиме «дельта» необходимо указать квалификатор ?delta после имени объекта.

Следующий пример показывает чтение объекта в режиме «дельта». На первой консоли создаём объект:

# echo "color::red\nform::square" >> /fs/pps/figure

На второй консоли подписываемся на него в режиме «дельта»:

# cat /fs/pps/figure?delta,wait 
@figure
color::red
form::square

Меняем цвет:

# echo "color::green" >> /fs/pps/figure

Наблюдаем изменения:

@figure
color::green

Меняем форму:

# echo "form::circle" >> /fs/pps/figure

Наблюдаем изменения:

@figure
form::circle

Меняем сразу два свойства:

# echo "color::red\nform::square" >> /fs/pps/figure

И получаем одно изменение:

@figure
color::red
form::square

Режим сервера


У объекта PPS может быть сервер, т.е. такой процесс, который является главным среди издателей. Он также называется критическим подписчиком. Чтобы открыть объект в режиме сервера необходимо указать квалификатор ?server. Остальные подписчики становятся как бы клиентами.

Если любой из клиентов изменит свойство объекта, то уведомление и изменение объекта получит только сервер. К идентификатору объекта добавляется также идентификатор клиента (число), например:

@figure.1234

Если на объект подписывается новый клиент, то сервер получает уведомление со знаком плюс (+):

+@figure.1234

Аналогичное уведомление приходит, если клиент отписался от объекта, только со знаком минус (-). Если сервер хочет ответить только клиенту, то он должен записать объект с идентификатором клиента, в противном случае уведомление получат все клиенты. На рис. 2 показано взаимодействие сервера и клиента PPS.

image
Рис. 2. Режим сервера PPS.

Пример работы сервера затруднительно показать используя только утилиты командной строки, так что, в случае интереса, предлагается разработать его самостоятельно, например, на C.

Поддержка PPS в различных языках программирования


Основным языком программирования в QNX является C. Но поддерживаются и другие языки и платформы. Будут ли они поддерживать PPS? Да, будут. Для такой стратегической платформы, как Adobe Flash, есть расширение и классы для работы с PPS. А как быть с другими языками?

Как можно было убедиться из всего, что было рассказано в этой заметке, для взаимодействия через PPS достаточно только уметь открывать файлы, читать из и писать в них. Для комфортной работы понадобится реализация функции select(). Вот, наверное, и все требования. И этим требованиям отвечает, например, Python. Не верите? Да нет ничего проще, давайте смело создадим два объекта PPS при помощи следующих команд:

# echo "form::triangle\ncolor::green" >> /fs/pps/figure
# echo verb::runs >> /fs/pps/action

В другой консоли запустим следующий скрипт на Python:

#!/usr/qnx650/host/qnx6/x86/usr/bin/python

import select

action = { "verb" : "stop" }
figure = { "form" : "circle", "color" : "black" }
sentence = { "action" : action, "figure" : figure }

f1 = open('/fs/pps/figure?delta', 'r')
f2 = open('/fs/pps/action?delta', 'r')

while 1:
        r, w, e = select.select( [f1, f2], [], [] )
        for f in r:
                d = f.read().split()
                for a in d[1:]:
                        k, v = a.split( "::" )
                        sentence[ d[0][1:] ][k] = v

        print figure["color"], figure["form"], action["verb"]

В первой консоли попробуем менять разные атрибуты объектов figure и action, а во второй будем наблюдать изменение вывода. Попробуйте сами поиграться, у вас должен получиться примерно следующий вывод:

green triangle runs
green circle runs
blue circle runs
white square runs
white square stops

Скрипт на Python достаточно простой и служит только для иллюстрации возможности работы различных языков программирования с PPS. Нет так называемой защиты от дурака. Но обратите внимание, насколько незатейливая программа получилась. Вот это да, не надо ничего особенного делать для взаимодействия процессов друг с другом. Лично мне очень понравилась технология PPS.

Дополнительные материалы


  1. Технология PPS
  2. Устойчивая служба публикации/подписки QNX
  3. QNX Persistent Publish/Subscribe Developer's Guide


1 Стоит отметить режим сервера, который рассматривается отдельно.

2 В случае перезагрузки операционной системы изменения накопленные в режиме дельта будут утеряны.
Tags:
Hubs:
Total votes 19: ↑18 and ↓1 +17
Views 6.5K
Comments Comments 11