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
переменные на самом деле служат?
Еще несколько небольших вопросов:
- Почему функции не имеют никаких типов возврата, даже
void
s? - Когда порт Erlang отправляет данные в C, первый байт определяет функцию, которая будет вызвана. Если байт содержит десятичную 1, то
foo()
называется, если байт содержит десятичную 2, тоbar()
называется. Если его не изменить, этот протокол может быть использован для вызова до 255 различных функций C с одним параметром в каждой. Это правильно? - "Добавление индикатора длины будет выполнено автоматически портом Erlang, но должно быть сделано явно во внешней программе на Си". Что это значит? На какой строке кода это делается?
- Из учебника: "По умолчанию программа на C должна читать со стандартного ввода (дескриптор файла 0) и записывать в стандартный вывод (дескриптор файла 1)". Затем: "Обратите внимание, что stdin и stdout предназначены для буферизованного ввода / вывода и не должны использоваться для связи с Erlang!" В чем тут подвох?
- Зачем
buf
инициализируется в[100]
?
1 ответ
Этот ответ также не принимается во внимание (я не программист на Erlang или C, я просто изучаю один и тот же материал)
Ваша начальная модель немного отличается. Код работает на самом деле, читая первые два байта из stdin
, предполагая, что это означает длину фактического сообщения, затем считывая, что еще много байтов из stdin
, В этом конкретном случае случается, что фактическое сообщение всегда составляет два байта (число, соответствующее функции, и один целочисленный аргумент для передачи ей).
0 - а) read_exact
читает len
байты из stdin
, read_cmd
использования read_exact
сначала определить, сколько байтов должно быть прочитано (либо число, обозначаемое первыми двумя байтами, либо ни одного, если доступно менее двух байтов), а затем прочитать это количество байтов. write_exact
пишет len
байтов в stdout
, write_cmd
использования write_exact
вывести двухбайтовый заголовок, за которым следует сообщение (надеюсь) соответствующей длины.
0 - б) я думаю 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 или выше, подойдет, но я мог бы быть исправлен в этом вопросе.