Роб Кёртен - Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
}
Отметьте несколько вещей.
В функции io_msg() для «инкапсуляции» специального сообщения (передаваемого в параметре «msg») в структуру io_message использован двухкомпонентный вектор ввода-вывода IOV.
Структура io_message была предварительно обнулена, и в ней был задан тип сообщения (_IO_MSG), а также инициализировано поле cmd (это будет использовано администратором ресурса для определения типа посылаемого сообщения).
В качестве кода завершения функции io_msg() использовался непосредственно код завершения MsgSendv().
Единственная «забавная» вещь, которую мы тут сделали, касается поля mgrid. QSSL резервирует для данного поля диапазон значений со специальным поддиапазоном для «неофициальных» драйверов. Этот поддиапазон ограничен значениями от _IOMGR_PRIVATE_BASE до IOMGR_PRIVATE_MAX соответственно. Если вы разрабатываете глубоко встраиваемую систему и хотите быть уверены, что ваш администратор ресурса не получит никаких неподходящих сообщений, то смело можете использовать значения из этого специального диапазона. С другой стороны, если вы разрабатываете в большей степени «настольную» или «обычную» систему, вы можете захотеть точно проконтролировать, будут ли вашему администратору ресурса приходит несоответствующие сообщения или нет. В этом случае вам нужно будет обратиться в QSSL за значением mgrid, зарезервированным специально для вас — никто, кроме вас, не должен использовать это номер. Посмотрите файл <sys/iomgr.h>, там представлены используемые в настоящее время диапазоны. В нашем вышепредставленном мы могли предположить, что COMMAND_XYZ базирована на _IOMGR_PRIVATE_BASE:
#define COMMAND_XYZ (_IOMGR_PRIVATE_BASE + 0x0007)
или QSSL назначила нам специальный поддиапазон:
#define COMMAND_XYZ ( IOMGR_ACME_CORP + 0x0007)
«Клиент/сервер» с использованием администратора ввода/выводаА что если клиент, которого вы переносите, использует администратор ввода/вывода? Как адаптировать его для QNX/ Neutrino? Ответ прост: мы уже это сделали. Установив интерфейс на основе файловых дескрипторов, мы уже используем администратор ресурса. В QNX/Neutrino вам почти никогда не придется использовать интерфейс «сырых» сообщений. Почему?
1. Вам пришлось бы самим беспокоиться о сообщении _IO_CONNECT, поступившим с клиентским вызовом open(), или искать способ поиска администратор ресурса, альтернативный использованию open().
2. Вам пришлось бы искать способ сопоставить клиенту конкретный контекстный блок в администраторе ресурса. Это, конечно, не ракетная техника, но поработать придется.
3. Вам придется инкапсулировать все ваши сообщения вручную вместо использования стандартных POSIX-функций, которые бы сделали эту работу за вас.
4. Ваш администратор ресурса не будет работать с приложениями на основе stdin/stdout. В примере с аудиодрайвером вы не смогли бы просто так выполнить mp3_decode spud.mp3 >/dev/audio, потому что open(), скорее всего, не сработала бы (а если бы и сработала, то не сработала бы write(), и так далее).
ПроксиВ QNX4 единственным способом передачи неблокирующего сообщения было создание прокси — это делалось с помощью функции qnx_proxy_attach(). Эта функция возвращает идентификатор прокси (proxy ID), (он выбирается из того же самого пространства номеров, что и идентификаторы процессов), к которому вы затем можете применить функцию Trigger() или возвратить его из функции обработки прерывания (см. ниже).
В QNX/Neutrino вы бы вместо этого настроили структуру struct sigevent на генерацию «импульса», а потом либо использовали бы функцию MsgDeliverEvent() для доставки события, либо привязали бы событие к таймеру или обработчику прерывания.
Обычный прием распознавания прокси-сообщений QNX4 (полученных с помощью Receive() или Creceive()) — сравнить идентификатор процесса, возвращенный функцией приема сообщения, с ожидаемым идентификатором прокси. Если совпадают — значит, это прокси. Как вариант, идентификатор процесса можно было игнорировать и обрабатывать сообщение как «стандартное». К сожалению, это несколько усложняет проблему переноса программ.
Анализ прокси по идентификаторамЕсли вы сравниваете полученный от функции приема идентификатор процесса со списком ожидаемых идентификаторов прокси, обычно вы игнорируете содержимое прокси. В конце концов, коль скоро содержимое прокси нельзя изменить после ее создания, какой прок с анализа сообщения, о котором вы уже знаете, что это одна из ваших прокси? Вы можете возразить, что это для удобства — помещаем в прокси нужные сообщения, а затем обрабатываем все сообщения одним стандартным декодером. Если это ваш случай, см. ниже «Анализ прокси по содержимому».
Поэтому, в QNX4 ваш код выглядел бы примерно так:
pid = Receive(0, &msg, sizeof(msg));
if (pid == proxyPidTimer) {
// Сработал наш таймер, сделать что-нибудь
} else if (pid == proxyPidISR) {
// Сработал наш ISR, сделать что-нибудь
} else {
// Не наша прокси — возможно, обычное
// клиентское сообщение. Сделать что-нибудь.
}
В QNX/Neutrino он заменился бы на следующий:
rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
if (rcvid == 0) { // 0 значит, что это импульс
switch (msg.pulse.code) {
case MyCodeTimer:
// Сработал наш таймер, сделать что-нибудь
break;
case MyCodeISR:
// Сработал наш ISR, сделать что-нибудь
break;
default:
// Неизвестный код импульса
break;
}
} else {
// rcvid - не нуль, значит, это обычное
// клиентское сообщение. Сделать что-нибудь.
}
Отметим, что это пример для случая, когда вы обрабатываете сообщения самостоятельно. Но поскольку мы рекомендуем использовать библиотеку администратора ресурсов, на самом деле ваша программа выглядела бы примерно так:
int main(int argc, char **argv) {
...
// Выполнить обычные инициализации
pulse_attach(dpp, 0, MyCodeTimer, my_timer_pulse_handler,
NULL);
pulse_attach(dpp, 0, MyCodeISR, my_isr_pulse_handler,
NULL);
...
}
На этот раз мы предписываем библиотеке администратора ресурсов ввести две проверки из предыдущего примера в основной цикл приема сообщений и вызывать две наши функции обработки (my_timer_pulse_handler() и my_isr_pulse_handler()) всякий раз, когда обнаруживаются нужные коды. Гораздо проще.
Анализ прокси по содержимомуЕсли вы анализируете содержимое прокси (фактически игнорируя, что это прокси, и обрабатывая их как сообщения), то вы автоматически имеете дело с тем, что в QNX4 на прокси ответить нельзя. В QNX/Neutrino ответить на импульс тоже нельзя. Это означает, что у вас уже есть код, который либо анализирует идентификатор, возвращаемый функцией приема, и определяет, что это прокси, и отвечать не надо, либо смотрит на содержимое сообщения и по нему определяет, надо отвечать на это сообщение или нет.
К сожалению в QNX/Neutrino произвольные данные в импульс не запихнешь. Импульс имеет четко определенную структуру, и обойти это нельзя. Умным решением здесь было бы «имитировать» сообщение от прокси при помощи импульса и таблицы. Таблица содержала бы сообщения, которые раньше передавались посредством прокси. Получив импульс, вы использовали бы поле value в качестве индекса к этой таблице, выбрали бы из таблицы соответствующее сообщение и «притворились», что получено именно оно.
Обработчики прерываний
Обработчики прерываний в QNX4 могли либо возвратить идентификатор прокси (указывая этим, что надо переключить прокси и таким образом уведомить ее владельца о прерывании), либо возвратить нуль (что означало бы, что в дальнейшем ничего делать не требуется). В QNX/Neutrino механизм почти идентичен — за исключением того, что вместо возвращения идентификатора прокси вы возвращаете указатель на struct sigevent. Генерируемое событие может быть либо импульсом (ближайший аналог прокси), либо сигналом, либо созданием потока — как выберете, так и будет.
Также в QNX4 вы обязаны были иметь обработчик прерывания — даже в том случае, если он должен был только возвратить идентификатор прокси. В QNX/Neutrino вы можете привязать struct sigevent к вектору прерывания, используя InterruptAttachEvent(), и это событие будет генерироваться при каждой активизации данного вектора.
Резюме
Перенос приложений из QNX4 в QNX/Neutrino или поддержка программы, функционирующей в обеих операционных системах, — это возможно, если придерживаться следующих правил: