QNX RTP Logo QNX Realtime Platform: Русский Портал QNX
Thursday, 20 Nov 2008 16:17
Меню

Проект OpenNET - все о Unix
Разработка структуры системы для среды, основанной на обмене сообщениями Print E-mail
Rob Krten, перевод: Артем Давыдов

Знать то, что QNX Neutrino основано на обмене сообщениями - это одно, понимать же преимущества этого при разработке своих программ - это совершенно другое. Для таких стандартных UNIX- приложений, как HTTP веб-сервер, или протоколирование событий, это знать совершенно не обязательно. Понимание архитектуры, основанной на обмене сообщениями, приобретает большую ценность при разработке своей собственной, совершенно новой, системы. Главная задача, которая возникает при разработке таких систем - это создание структуры системы и становится очевидным, что вы должны дать ответ на следующие вопросы:

  • Какую работу должен выполнять один процесс, т.е. как разграничить функциональность (границы ответственности) процессов?
  • Каким образом построить драйверы для моего аппаратного обеспечения?
  • Как создать большую систему в модульном исполнении?
  • Устойчива ли такая структура к действию времени, т.е. будет ли она работать в таком исполнении через два года, когда требования к ней изменятся?
  • Вот краткие ответы на эти вопросы:

  • Один процесс должен быть сфокусирован на одной задаче, оставляя все остальное другим процессам.
  • Драйверы должны быть написаны так, чтобы предоставлять удобный интерфейс к аппаратному обеспечению, но в то же время они не должны заставлять углубляться в тонкости конкретного аппаратного обеспечения, т.е. предоставлять "абстрактный" уровень общения с аппаратным обеспечением.
  • Чтобы создать большую систему, начните с нескольких небольших систем, с четко очерченными задачами, и "склейте" их вместе.
  • Если вы последуете этим рекомендациям, то получите систему, состоящую из "кирпичиков" которые вы позже сможете "складывать" и добавлять как угодно, и в какой угодно момент времени.

    Применяя все это в настоящем мире.

    Задачи.
    Давайте взглянем на все вышеперечисленные вопросы в свете потенциально возможной ситуации. Вы - разработчик программного обеспечения в компании, занимающейся вопросами безопасности, и перед вами поставлена задача создать программное обеспечение для новой системы безопасности. Аппаратное обеспечение состоит из считывателей магнитных карт, приводов дверных замков, а также различных датчиков (дыма, огня, движения, удара, и так далее).

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

    План.
    Ваша первая задача - рассмотреть систему в общем, и разработать структуру программного обеспечения. С точки зрения поставленных задач, требования к вашей системе таковы: должна присутствовать поддержка большого числа устройств, расположенных в различных помещениях, а также возможность, по мере необходимости, добавления новых, не только по количеству, но и по качеству (например, считыватели отпечатков пальцев, или сетчатки глаза), устройств.

    Итак, первым шагом будет определение границ ответственности процессов, или, говоря другими словами, определить, сколько работы должен выполнять один процесс.

    Если вы на минуту отвлечетесь от системы безопасности, которую мы с вами разрабатываем, и рассмотрите какую-нибудь систему управления базами данных, то увидите схожие черты. СУБД управляет базой данных и ей совершенно безразлично, на каком носителе находятся эти данные, и (в общем-то) все равно как организован этот носитель, во всяком случае, СУБД понятия не имеет о таблице разбиения жесткого диска, и, тем более, какой интерфейс у этого диска - SCSI или IDE. В таких системах СУБД использует набор абстрактных сервисов предоставляемых ей файловой системой. И СУБД не обязана знать ничего, кроме предоставляемых ей абстрактных сервисов, а тем более, как в конкретном случае построена цепочка абстрактных сервисов. Следующий уровень - файловая система - использует набор абстрактных сервисов, предоставляемых ей драйвером диска. И, наконец, драйвер диска управляет аппаратным обеспечением. Очевидное преимущество такой схемы в том, что верхние уровни в этой цепочке ничего не знают о тонкостях реализации нижних уровней. Таким образом, однажды определив интерфейс (набор абстрактных сервисов на каждом уровне) между приложениями в этой цепочке, вы можете использовать ваше программное обеспечение на большом спектре аппаратного обеспечения, только добавляя новые модули поддержки аппаратного обеспечения, и избежать необходимости переписывать все программное обеспечение целиком, "с нуля".

    Если теперь перенести все это на нашу систему безопасности, вы сможете незамедлительно начать с низа этой цепочки, а именно с аппаратного обеспечения. Вы знаете, что у вас имеются несколько типов устройств, и вы знаете, что программному обеспечению не требуется знать всех тонкостей реализации того или иного устройства. Таким образом, ваша первая задача "провести линию" на уровне аппаратных интерфейсов, т.е. вы создаете набор драйверов для аппаратного обеспечения, предварительно разработав для них стандартный API (набор абстрактных сервисов), который бы использовали программы более высокого уровня.

    Одной из таких программ, скорее всего, станет какое-то управляющее приложение. Например, этому приложению нужно будет проверить, разрешено ли мистеру Смиту войти в дверь номер 76 в 6:30, и если это подтвердится, разрешить ему войти. Вы можете заметить, что управляющей программе потребуется доступ к базе данных (для проверки прав доступа, и, возможно, протоколирования), также эта программа должна уметь получать данные со считывателя магнитных карт (чтобы знать, кто пытается пройти в эту дверь), и управлять дверными замками (чтобы открыть дверь).

    Теперь, когда мы определили эти два уровня, стоит разобраться с интерфейсами. Так как мы уже проанализировали требования приложения высокого уровня, мы сможем определить, какие требования предъявить к интерфейсам программ более низкого уровня. Для каждого считывателя магнитных карт вы должны получать следующую информацию: а). кто-то использовал считыватель, чтобы открыть дверь и б). где расположен этот считыватель. Вы также должны иметь возможность открывать и закрывать двери таким образом, чтобы мистер Смит мог войти, но дверь закрылась бы за ним.

    Система должна быть масштабируемой - т.е. она должна работать не только в маленьком офисе с несколькими устройствами, но и в большой компании с сотнями (если не тысячами) устройств.

    Если вам нужна масштабируемая система, то следует позаботиться о следующих вопросах.

  • Достаточно ли будет мощности центрального процессора для работы системы в максимальной конфигурации?
  • Хватит ли возможностей аппаратуры (PCI слоты, и т.д.)?
  • Как распределить вашу систему?
  • Есть ли какие-нибудь узкие места? Где они, и как их можно обойти?
  • Как вы можете заметить, все эти вопросы масштабируемости очень тесно связаны с тем, насколько хорошо вы проработали структуру вашей системы. Например, если вы возложили слишком много функций на один процесс (скажем, у вас один процесс контролирует все дверные замки), то вы ограничиваете свои возможности исполнять этот процесс на нескольких процессорах. С другой стороны, если вы слишком ограничите функциональность процесса, скажем, к примеру, один процесс обслуживает одну функцию одного замка, то у вас возникнет проблема чрезмерного обмена данными и задержки в работе.

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

    Приводы дверных замков.

    Давайте начнем с приводов дверных замков, так как это самые простые из устройств в нашем списке. Все что нам нужно от драйвера - это возможность разрешить открыть дверь, и возможность не дать ей открыться. Эти требования как раз и определяют те команды, которые должен воспринимать драйвер. Вам нужно будет сообщить драйверу, с каким замком он должен в данный момент работать и нужное состояние замка (открыт или закрыт). Это были бы две единственные команды, которые я бы использовал для начального прототипа программного обеспечения. Позже, чтобы облегчить работу и иметь поддержку нового оборудования, вы можете добавить и другие команды. Но предположим, что у вас есть два типа привода дверных замков. Один, является простым приводом замка, который во включенном состоянии позволяет открыть дверь, а в выключенном не позволяет этого сделать. Второй же, является дверным механизмом с моторизированным приводом, который во включенном состоянии при помощи двигателя открывает дверь, а в выключенном - позволяет двери закрыться. На первый взгляд может показаться, что вам потребуются два различных драйвера с двумя различными интерфейсами. Однако с этими устройствами можно работать при помощи единого интерфейса (но с различными драйверами). Все, что вы хотите сделать (на высоком уровне), это разрешить кому-то пройти через дверь. На самом деле, это означает, что вам требуется какой-то таймер, связанный с открыванием дверей. Когда доступ был разрешен, вы позволите двери оставаться открытой, скажем 20 секунд. Сразу после этого вы запираете дверь. Для разных типов дверей вы захотите увеличить, или уменьшить, это время.

    Ключевой вопрос, который здесь возникает, это где поместить этот таймер. Есть три возможных местоположения.

  • Сам драйвер дверного замка.
  • Отдельный драйвер - менеджер таймеров, общающийся с драйверами замков.
  • Сама управляющая программа.
  • И это опять вопрос о структуре системы (который очень тесно связан с масштабируемостью).

    С одной стороны, если вы встроите таймер в саму управляющую программу, вы дадите на нее дополнительную нагрузку. Ей придется выполнять не только свои обычные обязанности (работа с базой данных, отображение данных для оператора, и так далее), но еще и обслуживать таймеры. На небольшую, простую систему это, скорее всего, не окажет никакого влияния. Но если вы станете расширять вашу систему до масштабов предприятия, то столкнетесь с тем, что все дополнительные вычисления сосредоточены в едином месте. Где бы вы ни встретили фразу "сосредоточены в едином месте", вы сразу же должны обратить внимание на проблемы с масштабируемостью. Возникают несколько существенных проблем при встраивании таймеров в управляющую программу.

  • Масштабируемость - управляющая программа должна содержать свой таймер для каждой двери; чем больше дверей, тем больше таймеров.
  • Безопасность - если после открытия двери перестанет работать подсистема связи с замком, то вы останетесь с открытой дверью.
  • Возможность обновления - если новый тип двери потребует другие параметры для таймера (например, вместо простых команд "открыть" и "закрыть", ей требуется передать несколько команд для нескольких устройств с различной задержкой), то вам придется обновлять управляющую программу целиком.
  • Короткий ответ здесь - управляющая программа все-таки не должна содержать таймеры для замков. Таймеры - это та низкоуровневая деталь, которая идеально подходит для того, чтобы быть где-то еще.

    Рассмотрим следующий возможный вариант. Нужен ли вам отдельный процесс, который управляет таймерами и общается с драйверами дверного замка? И я снова отвечу - нет. Вопросы масштабируемости и безопасности, рассмотренные в предыдущем примере, в данном случае неприменимы (вы же, конечно, распределите этот менеджер таймеров между несколькими процессорами, так, что масштабирован он будет хорошо, и, так как он будет работать на том же процессоре, что и драйвер, можно исключить и вопрос об ошибках связи). Однако проблема с возможностью обновления остается. Но появляется еще и новая проблема - функциональная группировка. Вот что я здесь имею в виду. Таймер тесно связан с аппаратным обеспечением. У вас может быть простая аппаратура, для которой вы сами пишете таймер, или более сложная с уже встроенным таймером. Плюс к этому, у вас может быть и очень сложное оборудование, требующее многофазовые управляющие последовательности. Имея отдельный менеджер, работающий с таймерами, вы будете зависеть от тонкостей аппаратного обеспечения. Проблема здесь в том, что вы распределяете информацию об аппаратном обеспечении между двумя процессами, не получая при этом никаких преимуществ. Если бы это были драйверы файловой системы и диска, то получалось бы, что у вас один процесс отвечает за чтение данных с диска, а другой - за запись. Поэтому никаких преимуществ вы не получаете, и даже наоборот, усложняете драйвер, потому что теперь два процесса должны координировать свои действия друг с другом, чтобы получить доступ к аппаратному обеспечению.

    Но хочется отметить, что иногда, наличие дополнительного процесса между управляющей программой и определенным дверным замком все-таки имеет смысл. Предположим, что вы хотите создать "мета" драйвер дверных замков для какого-либо сложного последовательного доступа к дверям (например, для помещения с повышенной секретностью, где, открыв одну дверь, надо ее полностью закрыть, а уж потом открыть следующую, и так далее). В этом случае, мета драйвер будет на самом деле отвечать за переключение конкретных драйверов дверных замков. Положительной стороной того, как вы структурировали ваши устройства, является еще и то, что для управляющей программы этот мета драйвер выглядит как обычный драйвер дверного замка, а все подробности скрыты стандартным интерфейсом.

    Но вы еще не закончили!
    Вашей системе еще далеко до завершения. Мы только рассмотрели работу с дверными замками и проиллюстрировали некоторые общие структурные вопросы, которые должны быть решены. Вашей задачей, при создании драйверов дверных замков, было создать такой процесс, который бы выполнял необходимую работу в достаточной степени для обеспечения простого, прозрачного абстрактного интерфейса для программного обеспечения более высокого уровня. Вы использовали функциональную группировку (основываясь на возможностях аппаратного обеспечения), когда решали, какие функции можно поместить в драйвер, а какие оставить другим процессам. Косвенно вы познакомились с проблемами, возникающими при построении большой системы из набора более мелких, четко определенных, модулей, которые могут тестироваться и разрабатываться независимо друг от друга, что приводит к построению системы, которая позволит с легкостью добавлять новые типы модулей.

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

    Перевод: Артем Давыдов, Москва.

    [Вернуться к списку]
    ©   2000-2003 Команда проекта QNX.ORG.RU // QNX.ORG.RU Team
    Авторы проекта: Дмитрий Алексеев [dmi] и Дмитрий Васильев. Техническое сопровождение проекта: Игорь Сорокин [isorokin]. Информационное сопровождение: Дмитрий Алексеев [dmi]
    QNX - зарегистрированная торговая марка QNX Software Systems, Ltd., Canada. Остальные упоминаемые на сайте торговые марки и логотипы являются исключительно собственностью их уважаемых владельцев. Ничьи права не затронуты. Материалы сайта не могут быть скопированы и где-либо использованы в той или иной форме без письменного разрешения разработчиков сайта.
    Powered by Mambo Open Source