C и Erlang: пример порта Эрланг

Отказ от ответственности: автор вопроса имеет среднее знание Erlang и базовые знания C.

Теперь я читаю Руководство пользователя по совместимости. Я успешно скомпилировал пример complex.c, и он без проблем работает с портом Erlang.

Однако я хотел бы понять, как работает фактический код C. Я понимаю это в целом: в примере он считывает 2 байта со стандартного ввода и проверяет первый байт. В зависимости от первого байта он вызывает функцию foo или bar. Это предел моего понимания этого прямо сейчас.

Итак, если взять оба erl_comm.c:

/* erl_comm.c */

typedef unsigned char byte;

read_cmd(byte *buf)
{
  int len;

  if (read_exact(buf, 2) != 2)
    return(-1);
  len = (buf[0] << 8) | buf[1];
  return read_exact(buf, len);
}

write_cmd(byte *buf, int len)
{
  byte li;

  li = (len >> 8) & 0xff;
  write_exact(&li, 1);

  li = len & 0xff;
  write_exact(&li, 1);

  return write_exact(buf, len);
}

read_exact(byte *buf, int len)
{
  int i, got=0;

  do {
    if ((i = read(0, buf+got, len-got)) <= 0)
      return(i);
    got += i;
  } while (got<len);

  return(len);
}

write_exact(byte *buf, int len)
{
  int i, wrote = 0;

  do {
    if ((i = write(1, buf+wrote, len-wrote)) <= 0)
      return (i);
    wrote += i;
  } while (wrote<len);

  return (len);
}

и port.c:

/* port.c */

typedef unsigned char byte;

int main() {
  int fn, arg, res;
  byte buf[100];

  while (read_cmd(buf) > 0) {
    fn = buf[0];
    arg = buf[1];

    if (fn == 1) {
      res = foo(arg);
    } else if (fn == 2) {
      res = bar(arg);
    }

    buf[0] = res;
    write_cmd(buf, 1);
  }
}

Что там делает каждая функция? Какую цель действительно выполняют переменные li, len, i, wrote, got?

Еще несколько мелких вопросов:

  • Почему функции не имеют типов возврата, даже void s?
  • Когда порт Erlang отправляет данные на C, первый байт определяет вызываемую функцию. Если байт содержит десятичное число 1, тогда вызывается foo(), если байт содержит десятичную дробь 2, тогда вызывается bar(). Если это не изменилось, этот протокол можно использовать для вызова до 255 различных функций C только с одним параметром. Это правильно?
  • "Добавление индикатора длины будет выполняться автоматически через порт Erlang, но должно выполняться явно во внешней программе C". Что это значит? На какой строке кода это делается?
  • Из учебника: "По умолчанию программа C должна считывать со стандартного ввода (дескриптор файла 0) и записывать на стандартный вывод (дескриптор файла 1)". Затем: "Обратите внимание, что stdin и stdout предназначены для буферизованного ввода/вывода и не должны использоваться для связи с Erlang!" Какая уловка здесь?
  • почему buf инициализируется [100]?

Ответ 1

Этот ответ также отклонен (я не программист Erlang или C, я просто переживаю один и тот же материал)

Ваша начальная модель немного выключена. То, как работает код, - это чтение первых двух байтов из stdin, предполагая, что это означает длину фактического сообщения, а затем чтение этого большего количества байтов из stdin. В этом конкретном случае бывает, что фактическое сообщение всегда состоит из двух байтов (число, соответствующее функции и один целочисленный аргумент для передачи).

0 - a) read_exact читает len байты из stdin, read_cmd сначала использует read_exact, чтобы определить, сколько байтов он должен прочитать (либо число, обозначенное первые два байта или ни один, если имеется меньше двух байтов), а затем читать это количество байтов. write_exact записывает len bytes в stdout, write_cmd использует write_exact для вывода заголовка с длиной в два байта, за которым следует сообщение (надеюсь) соответствующей длины.

0 - b) Я думаю, что len достаточно подробно рассмотрено выше. li - это имя переменной, используемой для генерации этого двухбайтового заголовка для функции записи (я не могу выполнить шаг за шагом по операциям сдвига бит, но конечным результатом является то, что len представлен в первые два байта отправлены). i является промежуточной переменной, основной целью которой является обеспечение того, чтобы write и read не возвращали ошибку (если они это делают, этот код ошибки возвращается в результате read_exact/write_exact). wrote и got отслеживать, сколько байтов было записано/прочитано, причем контуры с конвейерами должны быть выше, чем len.

1 - Я на самом деле не уверен. Версии, с которыми я работал, имеют тип int, но в остальном идентичны. Я получил из главы 12 Программирование Erlang, а не ссылку, которую вы связываете.

2 - Это правильно, но точка протокола порта заключается в том, что вы можете изменить его для отправки разных аргументов (если вы отправляете произвольные аргументы, вероятно, было бы лучше всего просто используйте C Node, а не порты). Например, я изменил его тонко в недавнем фрагменте, так что он отправляет одну строку, так как у меня есть только одна функция, которую я хочу вызвать C, устраняя необходимость указания функции. Я должен также упомянуть, что если у вас есть система, которая должна вызывать более 255 различных операций, написанных на C, вы можете захотеть переосмыслить свою структуру (или просто перевести все девять и записать все это на C).

3 - Выполняется

read_cmd(byte *buf)
{
  int len;

  if (read_exact(buf, 2) != 2)   // HERE
    return(-1);                  // HERE
  len = (buf[0] << 8) | buf[1];  // HERE
  return read_exact(buf, len);
}

в функции read_cmd и

write_cmd(byte *buf, int len)
{
  byte li;

  li = (len >> 8) & 0xff;        // HERE
  write_exact(&li, 1);           // HERE

  li = len & 0xff;               // HERE
  write_exact(&li, 1);           // HERE

  return write_exact(buf, len);
}

в функции write_cmd. Я думаю, что объяснение приведено в 0 - a); что заголовок, который сообщает/узнает, как долго будет остальное сообщение (да, это означает, что это может быть только конечная длина, и эта длина должна быть выражена в двух байтах).

4 - Я не совсем уверен, почему это было бы уловкой здесь. Учиться разрабатывать?

5 - buf является байтовым массивом и должен быть явно ограничен (для целей управления памятью, я думаю). Я читал "100" здесь как "число, большее, чем максимальный размер сообщения, который мы планируем разместить". Фактическое число, выбранное, кажется произвольным, кажется, что что-либо 4 или выше, но я мог бы быть исправлен в этот момент.