Так какое же время видит клиент, когда он вызывает-таки функцию stat()
? Функция iofunc_stat_default(), предоставляемая библиотекой администратора ресурсов, посмотрит на поле flags атрибутной записи, чтобы проверить, являются времена доступа (поля atime, ctime и mtime) корректными или нет. Если нет (как это было бы после вызова io_read() с возвратом данных), iofunc_stat_default() устанавливает нужные из них в значение текущего времени.Этап 7
Теперь мы увеличиваем смещение lseek()
на число возвращенных клиенту байт, но делаем это только в том случае, если не обрабатываем модификатор _IO_XTYPE_OFFSET. Это гарантирует, что в случае отсутствия флага _IO_XTYPE_OFFSET, если клиент вызовет функцию lseek() для определения текущей позиции, или (более важный случай) если клиент вызовет read() для чтения еще нескольких байт, смещение в ресурсе будет корректным. Если _IO_XTYPE_OFFSET установлен, мы оставляем содержащееся в ocb смещение в покое.Этап 8
Сопоставьте этот этап с этапом 6. Здесь мы только разблокируем клиента и не выполняем больше никаких действий. Также обратите внимание, что функции MsgReply()
не передается никакой области данных, потому что в этом случае данные мы не возвращаем.Этап 9
И наконец, на этапе 9 мы выполняем действия, не зависящие от того, возвращаем мы данные клиенту или нет. Поскольку мы уже сами разблокировали клиента при помощи MsgReply()
, мы, конечно же, не хотим, чтобы это попыталась сделать еще и библиотека администратора ресурсов. Поэтому мы сообщаем ей, что мы уже сделали это сами, возвратом _RESMGR_NOREPLY.Эффективное применение других функций обмена сообщениями
Как вы помните из главы «Обмен сообщениями», мы упоминали еще несколько функций обмена сообщениями, а именно — функции MsgWrite()
, MsgWritev() и MsgReplyv(). Повод, в связи с которым я снова упоминаю здесь эти функции, состоит в том, что ваша функция io_read() может быть превосходным местом для их применения. В простом примере, показанном выше, мы возвращали непрерывный массив данных из постоянного места в памяти. В реальной же жизни вам может понадобиться возвратить, скажем, множество фрагментов данных из различных выделенных вами буферов. Классическим примером такого случая является циклический буфер, который часто применяется, например, в драйверах последовательных устройств. Часть данных может быть размещена в конце буфера, другая часть — в начале. В этом случае для возврата обеих частей данных вам понадобилось бы передать MsgReplyv() двухэлементный вектор ввода/вывода (IOV), где первый элемент содержал бы адрес (и длину) «нижней» части данных, а второй — адрес (и длину) «верхней» части. Или же, если вы ожидаете прибытия данных частями, вы могли бы вместо этого использовать функции MsgWrite() или MsgWritev() для записи данных в адресное пространство клиента по мере их поступления, а затем выдать заключительный вызов MsgReply() или MsgReplyv(), чтобы разблокировать клиента. Как мы уже показали выше, функция MsgReply() может и не передавать никаких данных— вы можете использовать ее просто для того, чтобы разблокировать клиента.Простой пример функции io_write()
Это был простой пример функции io_read(); давайте теперь перейдем к функции io_write()
. Основной камень преткновения, связанный с io_write(), — получить доступ к данным. Поскольку библиотека администратора ресурсов считывает лишь незначительную часть сообщения от клиента, переданные клиентом данные (они идут сразу после заголовка _IO_WRITE) могут быть приняты функцией io_write() только частично. Простой пример — представьте себе клиента, записывающего один мегабайт. Библиотекой администратора ресурсов будут считаны только заголовок сообщения и несколько байт данных. Остальная часть мегабайта остается по-прежнему доступной на клиентской стороне — администратор ресурсов при желании может к ней обращаться.Реально рассмотрения заслуживают только два случая:
• все содержимое сообщения клиентской функции write()
было считано библиотекой администратора ресурсов полностью; или• этого не произошло.
Судьбоносное решение, однако, состоит в ответе на следующий вопрос: «Какие проблемы сопряжены с попыткой сохранить полученную с первым сообщением часть данных?» Ответ такой: овчинка не стоит выделки. Тому есть ряд причин:
• обмен сообщениями (операции копирования на уровне ядра) выполняется очень быстро;
• проверка, получены ли данные целиком или частично, влечет определенные накладные расходы;