DragonFly - модель портов и сообщений

Оригинал: DragonFly - The Port/Messaging Model
Перевод: Валерий Винник, 05.01.25

DragonFly будет иметь упрощённый интерфейс работы с портами и сообщениями, соответствующий легковесным потокам ядра (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);  
   }
  }

Интерфейс обработки сообщений придаст этому механизму функции синхронной и асинхронной работы с сообщениями. Например, 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(), которые не предполагают преобразования, так что - насколько это касается отправителя и получателя - сообщение будет локальным в их контексте виртуальной памяти.

Обновлено: 12.03.2015