Этот вопрос возникает (должен возникать!) у каждого, кто приступает к разработке реального проекта, особенно если функциональность проекта распределяется между несколькими автономными процессами. Такая структуризация и вовсе не привычна разработчикам, приходящим из мира Windows. Для UNIX создание проектов, в которых порождается несколько процессов, такая структуризация уже гораздо органичнее, но и там это чаще всего лишь клонирование образа единого серверного процесса посредством fork()
. QNX предоставляет возможность идти еще дальше в построении приложений, представленных (разделенных) как группа разнородных взаимодействующих процессов:
• Уже полученные нами ранее тестовые результаты времени диспетчеризации и переключений контекстов (пусть даже они и сделаны бегло, только в качестве оценочных ориентиров) показывают, что представления приложения в качестве единого, монолитного процесса или процесса, содержащего группу потоков, либо просто разбиение приложения на группу процессов по производительности если и не эквивалентны, то крайне близки. Этот фактор не должен быть определяющим, и при структурировании приложения следует руководствоваться целесообразностью и удобством.
• Процессы QNX сохраняют все качества таковых и в UNIX вообще: они являются изолированными сущностями, которые взаимодействуют, если это необходимо, используя достаточно тяжеловесные (расточительные) механизмы IPC. Собственно, в этом и ценность процессов с их изолированными адресными пространствами — это механизм обеспечения высокой надежности и живучести приложений. Но QNX, не сужая спектр общепринятых IPC-механизмов, привносит совершенно новый «слой» инструментария взаимодействия — обмен сообщениями микроядра. При этом «проницаемость» процессов как отдельных клеток живого организма становится много выше, нисколько не снижая их «защищенности».
Но у нас есть две принципиально различные альтернативы для выражения этого «слоя» взаимодействий в своем программном коде: базовый механизм обмена сообщениями (низкоуровневая техника, известная еще со времен QNX 4.X) и механизм менеджера ресурса. Делать выбор между ними приходится на этапе раннего эскизного проектирования системы, поскольку перестраивать систему с одного механизма на другой в ходе развития — достаточно трудоемкий процесс, который может потребовать пересмотра и архитектурных основ развиваемого проекта.
Поэтому, приступая к проектированию нового проекта, мы должны априорно, до начала фактической разработки, отчетливо представлять, что выигрываем и что проигрываем, используя тот или иной механизм реализации обмена сообщениями.
Две стороны единого механизма
При рассмотрении базовой для QNX (собственно, для всех микроядерных ОС) техники обмена сообщениями в сравнении с технологией написания менеджеров ресурсов не покидает ощущение поразительной схожести происходящих в обоих случаях процессов. В этом нет ничего удивительного, поскольку инструментарий менеджеров ресурсов — это только система внешних «оберток» над базовым механизмом обмена сообщениями.
Для эффективного применения той или иной альтернативной технологии мы должны иметь возможность проанализировать многие сравнительные показатели выбираемого инструментария: простота, гибкость, эффективность, трудоемкость реализации, возможности внесения изменений при развитии проекта и на этапе его последующего сопровождения. Этим мы и займемся в оставшейся части главы.
Механизм прямого обмена сообщениями крайне просто выражается в программном коде. Когда достигнута полная ясность в значениях адресных параметров обмена, необходимо всего лишь несколько операторов, чтобы заставить все это «крутиться».
Со стороны сервера, например, это выглядит так:
int chid = ChannelCreate(0);
...
while (true) {
struct _msg_info info;
int rcvid = MsgReceive(chid, &bufin, sizeof(bufin), &info);
if (rcvid < 0) exit(EXIT_FAILURE);
if (MsgReply(rcvid, EOK, &bufou, sizeof(bufou) < 0) exit(EXIT_FAILURE);
}
Co стороны клиента:
int coid = ConnectAttach(node, pid, chid, _NTO_SIDE_CHANNEL, 0);
if (coid < 0) exit(EXIT_FAILURE);
...
while(...)
if (MsgSend(coid, &bufou, sizeof(bufou), &bufin, sizeof(bufin)) == -1)
exit(EXIT_FAILURE);
}
Код для реализации того же обмена, но организованного как менеджер ресурса, будет как минимум в несколько раз объемнее (образцы менеджеров мы уже видели ранее по тексту). Кроме того, по большей части он будет состоять из заполнения полей некоторых внутренних структур, используемых библиотеками менеджера ресурсов или пула потоков. На первый поверхностный взгляд такой код маловразумителен.
Читать дальше
Конец ознакомительного отрывка
Купить книгу