Функция MsgDeliverEvent()
Функция MsgDeliverEvent()
Как было упомянуто выше в параграфе «Иерархический принцип обмена», существуют ситуации, когда приходится нарушать естественное направление передач.
Такой случай возможен, когда у вас есть клиент, который посылает серверу сообщение и при этом не хочет блокироваться, а результат может быть доступен только через некоторое время. Конечно, вы могли бы частично решить эту проблему путем применения многопоточных клиентов, выделяя в клиенте отдельный поток для блокирующих вызовов сервера, но это не всегда с успехом работает в больших системах, поскольку при большом количестве серверов количество ждущих потоков было бы слишком велико. Но допустим, вы не хотите здесь использовать многопоточность, а вместо этого вам нужно, чтобы сервер ответил клиенту сразу, и чем-то вроде «Заказ принят; я скоро вернусь». Здесь, поскольку сервер ответил, клиент теперь свободен продолжать свою работу. После того как сервер отработает запрос клиента, ему потребуется как-то сказать клиенту «Проснись, вот твой заказ.» Очевидно, как мы это уже видели при анализе иерархического принципа обмена, сервер не должен передавать сообщения клиенту, потому что если клиент в это же время отправит сообщение серверу, это может вызывать взаимную блокировку. Так как же сервер может послать сообщение клиенту без нарушения иерархического принципа?
В действительности это составная операция. Вот как это работает:
1. Клиент создает структуру типа struct sigevent и заполняет ее.
2. Клиент посылает сообщение серверу, в котором запрашивает: «Сделай для меня то-то, ответ дай сразу же, а по окончании работы уведоми меня об этом при помощи структуры struct sigevent — структуру прилагаю».
3. Сервер принимает сообщение (которое включает в себя структуру struct sigevent), сохраняет структуру struct sigevent и идентификатор отправителя и немедленно отвечает клиенту.
4. Теперь клиент выполняется — как и сервер.
5. Когда сервер завершает работу, он использует функцию MsgDeliverEvent(), чтобы сообщить об этом клиенту.
Мы рассмотрим более подробно структуру struct sigevent в главе «Часы, таймеры и периодические уведомления», в параграфе «Как заполнить структуру struct sigevent», а здесь мы только предположим, что структура struct sigevent — это «черный ящик», который содержит некоторое событие, используемое сервером для уведомления клиента.
Поскольку сервер хранит клиентские struct sigevent и идентификатор отправителя, он теперь сервер может вызвать функцию MsgDeliverEvent(), чтобы доставить событие клиенту, как клиент того и желал:
int MsgDeliverEvent(int rcvid, const struct sigevent *event);
Обратите внимание, что функция MsgDeliverEvent() принимает два параметра — идентификатор отправителя (rcvid) и доставляемое событие (event). Сервер никогда не изменяет и даже не читает событие! Этот момент важен, потому что это позволяет серверу доставлять события вне зависимости от их выбранного клиентом типа, без какой бы то ни было специальной обработки на стороне сервера.
Идентификатор rcvid — это идентификатор отправителя, который сервер получил от клиента. Заметьте, что это определенно особый случай. Обычно, после того как сервер ответил клиенту, идентификатор отправителя прекращает иметь значение (потому что клиент уже разблокирован, и сервер не может разблокировать его заново или считать/записать данные, и т.п.). Но в нашем случае, идентификатор отправителя содержит только информацию для ядра, какому клиенту должно быть доставлено событие. Вызывая MsgDeliverEvent(), сервер не блокируется — для сервера это неблокирующий вызов. Ядро доставляет событие клиенту, после чего тот выполняет какие бы то ни было соответствующие действия.