Вспомните: в наших примерах мы использовали символьные имена узлов, например, /net/wintermute
. В QNX4 (предыдущая версия QNX до появления QNX/Neutrino) вся работа в сети была основана на концепции идентификатора узла, небольшого целого числа, уникально определяющего узел сети. Таким образом, в терминах QNX4 мы говорили бы что-то вроде «узел 61», или «узел 1», и это отражалось бы и на вызовах функций тоже.
При работе в QNX/Neutrino все узлы внутренне представляются 32-разрядными числами, но эти числа не являются уникальными в сети! Я имею в виду, что узел wintermute
может думать об узле spud
как об узле с дескриптором 7, в то время как сам узел spud
может думать, что дескриптор 7 соответствует узлу magenta
. Поясню подробнее, чтобы дать полную картину происходящего. В приведенной ниже таблице сведены примерные дескрипторы узлов, которые могли бы использоваться для описания трех узлов: wintermute
, spud
и foobar
(не путать с аббревиатурой FUBAR — прим. ред. :-):
Узел | wintermute |
spud |
foobar |
---|---|---|---|
wintermute | 0 | 7 | 4 |
spud | 4 | 0 | 6 |
foobar | 5 | 7 | 0 |
Обратите внимание, что каждый узел считает свой собственный дескриптор нулевым. Также отметьте, что для узла spud
оба узла wintermute
и foobar
имеют дескриптор 7. Однако, для узла foobar
узел wintermute
имеет дескриптор 4, а узел spud
— 6. Как я и упоминал раньше, эти номера не уникальны в сети, но они уникальны на каждом узле. Вы можете относиться к ним, как к файловым дескрипторам — два процесса, когда обращаются к одному и тому же файлу, могут иметь для него как одинаковый дескриптор, так и нет — все зависит от того, кто, когда и который файл открывает.
К счастью, вам не надо беспокоиться о дескрипторах узлов по ряду причин:
• Большинство осуществляемых вами операций обмена сообщениями «с внешним миром» будут реализовываться с помощью вызовов функций высокого уровня (таких как функция open(), приведенная в примере выше).
• Дескрипторы узлов не кэшируются — предполагается, что получив дескриптор, вы используете немедленно и забудете про него.
• Существует ряд библиотечных функций, предназначенных для преобразования имени пути (например, /net/magenta
) в дескриптор узла.
Чтобы работать с дескрипторами узлов, вам понадобится подключить файл <sys/netmgr.h>
, потому что он содержит прототипы семейства функций netmgr_*().
Для преобразования строки в дескриптор узла используется функция netmgr_strtond(). После получения дескриптора узла его следует сразу же применить в вызове функции ConnectAttach(). Не пытайтесь сохранять его какой-либо структуре данных! Веским основанием для этого является то, что администратор сети может решить повторно использовать дескриптор после отключения всех соединений с узлом.
Так что если вы получили дескриптор «7» для узла /net/magenta
, подсоединились к нему, передали сообщение и затем отсоединились, то существует возможность того, что администратор сети заново назначит дескриптор «7» другому узлу.
Поскольку дескрипторы узлов в сети не уникальны, возникает вопрос: «А как передавать эти штуки по сети?» Очевидно, взгляды узла magenta
и узла wintermute
на дескриптор «7» будут радикально отличаться. Существуют два способа решения этой проблемы:
• Не передавать по сети дескрипторы узлов и пользоваться символьными именами (например, /net/wintermute
).
• Применять функцию netmgr_remote_nd().
Первый метод хорош как универсальное решение. Второй метод достаточно удобен на практике.
int netmgr_remote_nd(int remote_nd, int local_nd);
Эта функция принимает два параметра, где remote_nd — дескриптор узла целевой машины, a local_nd — дескриптор узла, который нужно преобразовать из точки зрения локальной машины в точку зрения целевой. Результатом является дескриптор узла, корректный с точки зрения заданной удаленной машины.
Например, пусть wintermute
— имя нашей локальной машины. У нас есть дескриптор узла «7», который является корректным на нашей локальной машине и указывает на узел magenta
. Мы хотели бы выяснить, какой дескриптор узла использует узел magenta
для связи с нашим узлом:
int remote_nd;
int magenta_nd;
magenta_nd = netmgr_strtond("/net/magenta", NULL);
printf("ND узла magenta — %d\n", magenta_nd);
remote_nd = netmgr_remote_nd(magenta_nd, ND_LOCAL_NODE);
printf("С точки зрения узла magenta, наш ND — %d\n",
remote_nd);
Это программа могла бы вывести что-то вроде следующего:
ND узла magenta - 7
С точки зрения узла magenta, наш ND — 4
Это говорит о том, что на узле magenta
нашему узлу соответствует дескриптор «4». (Обратите внимание на использование специальной константы ND_LOCAL_NODE, которая в действительности равна нулю, для указания на «локальный узел»).
Теперь вернемся к тому, о чем мы говорили в разделе «Кто послал сообщение?»). Параметр struct _msg_info
содержит, среди всего прочего, два дескриптора узлов:
struct _msg_info {
int nd;
int srcnd;
...
};
Мы определили в описании для этих двух полей, что:
• nd — дескриптор принимающего узла с точки зрения передающего;
• srcnd — дескриптор передающего узла с точки зрения принимающего.
Так, для приведенного выше примера, где узел wintermute
— локальный, а узел magenta
— удаленный, когда узел magenta
посылает нам (узлу wintermute
) сообщение, эти поля заполняются следующим образом:
• nd равен 7;
• srcnd равен 4.
Наследование приоритетов
Одним из интересных моментов в операционных системах реального времени является феномен инверсии приоритетов.