#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(), и это событие будет генерироваться при каждой активизации данного вектора.