Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
С другой стороны, функция MsgReply может передавать данные (как видно из ее третьего и четвертого параметров) и заставляет функцию MsgSend клиента возвратить значение, переданное MsgReply в качестве второго аргумента. Переменная errno клиента остается нетронутой.
В общем случае, если вам нужно только сообщить о результатах действия («прошло/не прошло»), лучше применять функцию MsgError. Если бы вы возвращали данные, здесь была бы необходима функция MsgReply. Обычно, когда вы возвращаете данные, вторым параметром функции MsgReply
Ранее мы отметили, что для соединения с сервером функции ConnectAttach необходимо указать дескриптор узла (Node Descriptor — ND), идентификатор процесса (process ID — PID), а также идентификатор канала (Channel ID — CHID). До настоящего момента мы не обсуждали, как именно клиент находит эту информацию.
Если один процесс создает другой процесс, тогда это просто — вызов создания процесса возвращает идентификатор вновь созданного процесса. Создающий процесс может либо передать собственные PID и CHID вновь созданному процессу в командной строке, либо вновь созданный процесс может вызвать функцию getppid для получения идентификатора родительского процесса, и использовать некоторый «известный» идентификатор канала.
А что если у нас два совершенно чужих процесса? Это возможно, например, в том случае, если сервер создан некоей третьей стороной, а вашему приложению нужно уметь общаться с этим сервером. Реально мы должны найти ответ на вопрос: «Как сервер объявляет о своем местонахождении?»
Существует множество способов сделать это; мы рассмотрим только три из них, в порядке возрастания «элегантности»:
1. Открыть файла с известным именем и сохранить в нем ND/PID/CHID. Такой метод является традиционным для серверов UNIX, когда сервер открывает файл (например,
2. Использовать для объявления идентификаторов ND/PID/CHID глобальные переменные. Такой способ обычно применяется в многопоточных серверах, которые могут посылать сообщение сами себе. Этот вариант по самой своей природе является очень редким.
3. Занять часть пространства имен путей и стать администратором ресурсов. Мы поговорим об этом в главе «Администраторы ресурсов».
Первый подход относительно прост, но он чреват «загрязнением файловой системы», когда в каталоге
Имеется и другая связанная с этим подходом проблема. Поскольку процесс, который создал файл, может умереть, не удалив этот файл, то вы не сможете узнать, жив ли еще этот процесс, пока не попробуете передать ему сообщение. И это ещё не самое страшное — еще хуже, если комбинация ND/PID/CHID указанная в файле, оказывается настолько старой, что может быть повторно использована другой программой! Получив «чужое» сообщение, эта программа в лучшем случае его проигнорирует его, а ведь может и предпринять некорректные действия. Так что такой подход исключается.
Второй подход, где мы используем глобальные переменные для объявления значений ND/PID/CHID, не является общим решением проблемы, поскольку в нем предполагается способность клиента обратиться к этим глобальным переменным. А поскольку для этого требуется использование разделяемой памяти, это не будет работать в сети! Так что этот метод обычно используется либо в
Что реально происходит, так это то, что один поток в программе является клиентом, а другой поток — сервером. Поток-сервер создает канал и затем размещает идентификатор канала в глобальной переменной (идентификаторы узла и процесса являются одинаковыми для всех потоков в процессе, так что объявлять их не обязательно). Поток-клиент затем берет этот идентификатор канала и выполняет по нему функцию ConnectAttach.
Третий подход — сделать сервер администратором ресурса — является определенно самым прозрачным и поэтому рекомендуемым общим решением. Механизм того, как это делается, изложен в главе «Администраторы ресурсов», а пока все, что вы должны об этом знать — это то, что сервер регистрирует некое имя пути как свою «область ответственности», а клиенты обращаются к нему обычным вызовом функции open.
Файловые дескрипторы POSIX в QNX/Neutrino реализованы через идентификаторы соединений, то есть дескриптор файла уже является идентификатором соединения! Органичность этой схемы в том, что поскольку дескриптор файла, возвращаемый функцией open, фактически является идентификатором соединения, клиенту не нужно выполнять какие-либо дополнительные действия, чтобы использовать это соединение. Например, когда клиент после вызова open вызывает функцию read, передавая ей полученный дескриптор, это с минимальными накладными расходами транслируется в функцию MsgSend.
А что произойдет, если сообщение серверу передадут одновременно два процесса с разными приоритетами?
Если два процесса посылают сообщения «одновременно», первым доставляется сообщение от процесса с высшим приоритетом.
Если оба процесса имеют одинаковый приоритет, то сообщения будут доставлены в порядке отправки (поскольку в машине с одним процессором не бывает ничего одновременного, и даже в SMP-блоке будет присутствовать некий порядок, поскольку процессоры будут конкурировать между собой за доступ к ядру).
Мы еще вернемся к анализу других тонкостей этой проблемы чуть позже в этой главе, когда будем говорить о проблеме инверсии приоритетов.
До настоящего времени мы обсуждали основные примитивы обмена сообщениями. Как я и упоминал ранее, это минимум, который необходимо знать. Однако существует еще несколько дополнительных функций, которые делают нашу жизнь значительно проще.
Рассмотрим пример, в котором для обеспечения обмена сообщениями между клиентом и сервером нам понадобились бы и другие функции.
Клиент вызывает MsgSend для передачи неких данных серверу. После вызова MsgSend клиент блокируется. Теперь он ждет, чтобы сервер ему ответил.
Интересные события разворачиваются на стороне сервера. Сервер вызывает функцию MsgReceive для приема сообщения от клиента. В зависимости от того, как вы спроектировали вашу систему сообщений, сервер может знать, а может и не знать, насколько велико сообщение клиента. Как сервер может не знать, каков реальный размер сообщения? Возьмем наш пример с файловой системой. Предположим, что клиент делает так: