Оригинал: DragonFly - The Port/Messaging Model
Перевод: Lao, редакция от 05.06.01
1-я редакия от 05.01.25 - Unix.ginras.ru
В DragonFly будет иметь легковесный API портов и сообщений, сопутствующий легковесным нитям ядра (lightweight kernel threads). Концепция этого интерфейса очень проста: вы создаете сообщение, отправляете его на целевой порт и через некоторое время получаете ответ через порт ответа. Базируясь на этой простой абстракции, мы стремимся достичь высокого уровня гибкости и эффективности. Чтобы понять возможности системы передачи сообщений, сначала нужно уяснить, как диспетчеризуются сообщения. Обычно это делается примерно так:
fubar()
{
FuMsg msg;
initFuMsg(&msg, replyPort, ...);
error = targetPort->mp_SendMsg(&msg);
if (error == EASYNC) {
/* now or at some later time, or wait on reply port */
error = waitMsg(&msg);
}
}
API обмена сообщениями погрузит этот механизм в функции синхронной и асинхронной работы с сообщениями. Например, lwkt_domsg() отправит сообщение синхронно и будет ждать ответа. Будет установлен флаг для уведомления целевого порта о том, что данное соообщение должно блокироваться синхронно, и если целевой порт возвратит EASYNC, lwkt_domsg() будет блокироваться. А если lwkt_sendmsg() отправит сообщение асинхронно, но целевой порт возвратит код ошибки синхронизации (т.е. что-либо, отличное от EASYNC), lwkt_sendmsg() "вручную" поставит завершенное теперь сообщение в очередь к порту ответа.
Как вы можете догадаться, функция mp_SendMsg() целевого порта имеет полный контроль над тем, как порт поступает с сообщением. Независимо от уведомлений, поступивших на порт в виде флагов, целевой порт может "решить" обрабатывать и возвращать сообщения синхронно (в контексте отправителя) либо ставить сообщения в очередь и возвращать EASYNC. Работа с сообщениями, как правило, не должна "блокироваться" с точки зрения инициатора. Другими словами, целевой порт не должен стараться обрабатывать сообщение синхронно, если это может привести к блокировке. Вместо этого, целевой порт должен ставить сообщение в очередь к своей собственной нити (либо в очередь сообщений, встроенную в саму структуру целевого порта) и возвращать EASYNC.
Целевой порт мог бы обрабатывать сообщение синхронно по многим причинам. На самом деле, именно функция mp_sendMsg() целевого порта имеет дело с процессорными кэшами и операциями блокировки типа try_mplock(), стремясь обработать запрос без обращения к более ресурсоемким операциям постановки в очередь и переключения.
Главное здесь - это не забывать, что максимальная оптимизация, к которой мы стремимся, достигается за счет непосредственного исполнения mp_SendMsg() с накладными расходами, не превышающими накладные расходы на вызов простой подпрограммы. Никаких очередей, никакой "давки" у порта ответа... Если сообщение может обрабатываться синхронно, мы получаем исключительно дешевую операцию. Это и есть та изюминка, которая позволяет нам использовать описываемый интерфейс работы с сообщениями, более не беспокоясь о проблеме потери производительности. Мы явно НЕ используем усложненные методы, свойственные, например, Mach (имеется в виду Mach - разработанное в университете Карнеги-Меллона микроядро, используемое в некоторых UNIX-совместимых ОС, таких, как Hurd и Darwin; также основано на механизме обработки сообщений, поддерживает многозадачность и многопроцессорность - прим. перев.). Мы не стараемся отслеживать преобразования содержимого памяти, указатели и т.п., по крайней мере, на низком уровне интерфейса обработки сообщений. В интерфейсах обработки сообщений "пользователь"<->"ядро" просто используются векторы функции mp_SendMsg(), которые производят требуемую трансляцию, так что - насколько это касается отправителя и получателя - сообщение будет локальным в их контексте виртуальной памяти.