DragonFly - пользовательский API

Оригинал: DragonFly - User API
Перевод: Валерий Винник, 05.02.17

Создание портируемого интерфейса пользователя

В большинстве стандартных UNIX-систем используется таблица системных вызовов, посредством которой передаются вперёд-назад большинство типов данных, в том числе необработанные структуры. Крупнейшим препятствием на пути взаимодействия программ пользователя с ядрами более новыми или более старыми, чем сами программы, является частое изменение необработанных структур. Главными "возмутителями спокойствия" могут считаться сетевые интерфейсы, ioctls таблиц маршрутизации, ipfw и структуры необработанных процессов ps, vmstat и др. Но даже неописанные системные вызовы вроде stat() и readddir() имеют "шероховатости". В более общих словах, список системных вызовов сам может создать проблемы переносимости.

Целью настоящего проекта является: 1)сделать все текущие системные вызовы основанными на сообщениях; 2)передавать информацию о структуре посредством списков возможностей и элементов, а не в в иде необработанных структур; 3)ввести универсальный "средний слой", несколько напоминающий слой эмуляции, контролируемый ядром, но загружаемый в пользовательское пространство. Этот слой будет использовать все API стандартных системных вызовов, преобразуя их в соответствующие сообщения.

Например, эмуляция Linux будет выполняться скорее в (защищённом ядром) юзерланде, нежели в кернелланде. Эмуляция FreeBSD будет выполняться так же. Фактически, даже "родные" программы будут выполняться через слой эмуляции, чтобы увидеть системный вызов, который мы все знаем и любим. Единственное отличие в том, что родные программы будут знать о существовании слоя эмуляции, напрямую доступного из юзерланда, и не будут расходовать дополнительный INT0x80 (или какой его там) для входа в ядро только с тем, чтобы снова быть выброшенными в слой эмуляции.

Вторым крупным преимуществом преобразования системных вызовов в единицы, основанные на сообщениях, является полное разрешение вопроса потоков юзерланда. Для взаимодействия с множественными потоками юзерланда будет уже не нужно множественных контекстов ядра или стеков, потребуется только один контекст ядра и стек на каждый пользовательский процесс. Потоки юзерланда будут всё так же использовать rfork() для создания реальных процессов по каждому процессору системы, но все прочие операции будут использовать слой эмуляции, осведомлённый о потоках. Фактически, почти все запросы (upcalls) юзерланда будут исходить от слоя эмуляции в самом юзерланде, а не прямо от ядра. Вот пример работы эмуляционного слоя, осведомлённого о потоках:

  ssize_t
  read(int fd, void *buf, size_t nbytes)
  {
   syscall_any_msg_t msg;
   int error;
  
   /*
    * Use a convenient mostly pre-built message stored in
    * the userthread structure for synchronous requests.
    */
   msg = &curthread->td_sysmsg;
   msg->fd = fd;
   msg->buf = buf;
   msg->nbytes = bytes;
   if ((error = lwkt_domsg(&syscall_port, msg)) != 0) {
    curthread->td_errno = error;
    msg->result = -1;
   }
   return(msg->result);
  }

Вот именно. Единственные "настоящие" системные вызовы, с которыми будет оперировать DragonFly, будут представлены передающими сообщения примитивами отправки, получения и ожидания. Всё остальное будет идти через слой эмуляции. Конечно, на стороне ядра команда сообщения будет всплывать, попадая в таблицу организации размещения (dispatch table) почти такой же величины, как и та, что существует во FreeBSD 4.х. Но по мере того, как всё больше подсистем будут основаны на сообщениях, сообщения о системных вызовах будут становиться всё более интегрированными с этими подсистемами, и объём служебных сигналов операций с "сообщениями", способных всплыть в настоящий момент, будет меньше объёма служебных сигналов, образующегося при работе с дискретными системными вызовами. Переносимость достигается тогда намного легче, потому что слой эмуляции является как бы "чёрным ящиком", отделяющим ожидания программы юзерланда от ожиданий ядра, причём слой эмуляции может быть обновлён вместе с ядром (либо создана обратно совместимая версия), что делает вопросы переносимости прозрачными для бинарников юзерланда.

Кроме того, мы получаем все преимущества модели передачи сообщений, в том числе лёгкость проникновения в системные вызовы для отладки и других нужд, а также лёгкость создания слоя безопасности в ядре, который мог бы, например, отключать либо менять некоторые классы системных вызовов, основанных на обеспечении безопасности.

Обновлено: 12.03.2015