C и Erlang: пример порта 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 переменные?

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

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

person skanatek    schedule 07.05.2012    source источник


Ответы (1)


От этого ответа также отказываются (я не программист на Erlang или C, я просто просматриваю тот же материал)

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

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

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

1 – я не уверен. Версии, с которыми я работал, относятся к типу int, но в остальном идентичны. Я получил свой из главы 12 Programming 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 или выше, но я могу быть исправлен в этом вопросе.

person Inaimathi    schedule 08.05.2012
comment
Ваше предположение о buf верно. Это массив, т. е. непрерывная область памяти, способная хранить n элементов указанного типа. В этом случае память, выделенная в стеке. Другой способ выделения памяти состоит в том, чтобы сделать buf указателем и выделить его в куче с помощью malloc (но тогда вы должны сами free выделить память, когда закончите с этим). - person Emil Vikström; 08.05.2012
comment
Относительно 4. Во-первых, в учебнике говорится, что программа C должна читать из стандартного ввода (файловый дескриптор 0) и записывать в стандартный вывод (файловый дескриптор 1). Затем в учебнике говорится, что stdin/stdout не следует использовать для связи с Erlang. Разве это не прямое противоречие? Они предоставляют пример программы на C, которая использует стандартный ввод/стандартный вывод, а затем говорят, что ее не следует использовать, поскольку стандартный ввод/стандартный вывод буферизуется. Кажется, я что-то здесь упускаю. - person skanatek; 09.05.2012
comment
@MartinLee - О. Верно, я полагаю. Я понял, что вы не должны использовать эти порты для обратной связи между процессами Erlang и C (для этого и нужен узел C). Мы не хотя; Erlang отправляет один запрос и ожидает один ответ int на каждый вызов программы. Я думаю, что я мог неправильно понять или неправильно прочитать. - person Inaimathi; 09.05.2012
comment
Я прочитал упражнение в Programming Erlang, глава 12 — оно намного удобнее, чем онлайн-учебник; спасибо за ссылку. По поводу (0-a): а потом прочитать столько байтов - это вот эта строка: len = (buf[0] ‹‹ 8) | буф[1];? Как это работает? Вроде есть какая-то комбинация битшифта и битлогики, но я не понимаю, зачем она нужна для вычисления len. Более того, на 215 странице книги объясняется длина заголовка, но я не понимаю, почему мы должны использовать первые 2 байта в 0,3,2,45,32, чтобы получить длину пакета. Почему нельзя мы используем только 1 байт? - person skanatek; 10.05.2012
comment
@MartinLee - Как еще мы могли бы вычислить len? Я думаю, вы могли бы изменить протокол так, чтобы он использовал один байт и позволял интерпретировать его буквально как байт без знака, но это оставило бы вам максимальную длину сообщения 255. Длина 2 байта, вероятно, произвольна; некоторый идиосинкразический баланс между простотой хранения и длиной сообщения. - person Inaimathi; 10.05.2012
comment
Хорошо, я понял про длину. Я прочитал главу о битовом сдвиге в книге K&R и до сих пор не понимаю len = (buf[0] ‹‹ 8) | операция buf[1]. Хорошо, эта строка вычисляет len, но как она это делает? (единственное, что я понимаю, это то, что есть левый битовый сдвиг и побитовое ИЛИ). Что касается длины пакета (стр. 215 упражнения): первые два байта кодируют длину пакета. Далее следует результат 77 (снова 1 байт). Итак, пакет 0,2,77 имеет заголовок 0,2, который говорит, что длина пакета равна 1. Как эти два байта (0,2) говорят нам, что длина равна 1? - person skanatek; 13.05.2012
comment
Это связано с тем, как целые числа представлены на уровне байтов. Битовый сдвиг + побитовое ИЛИ преобразует два байта в одно значение int (хотя я думаю, что 0,2 фактически преобразуется в 2, а не в 1). продолжение - person Inaimathi; 13.05.2012
comment
Думайте с точки зрения задействованных битов. 0,2 -> [00000000], [00000010]. len — это целое число, так что на самом деле это 4 байта; [00000000:00000000:00000000:00000000]. Выполнение [00000000] << 8 сдвигает его влево на 8 бит (в данном случае это не имеет значения, поскольку первый бит равен 0), что дает вам [00000000:00000000]. Выполнение ([00000000] << 8) | [00000010] оставляет вас с [00000000:00000010], которое является битовым представлением короткого целого числа 2. Это присваивается len с двумя оставшимися байтами (так что технически вы можете объявить len как short integer без каких-либо побочных эффектов). - person Inaimathi; 13.05.2012
comment
Это было мое собственное понимание программиста, отличного от C, так что принимайте его с недоверием. Также полезно посмотреть это. Это примерно 5-минутный сегмент, в котором он конкретно говорит о шортах. Также есть около 10 минут довольно полезных примеров на ~20-минутной отметке. - person Inaimathi; 13.05.2012