2012-05-07 6 views
10

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

Я читаю Interoperability Tutorial User Guide прямо сейчас. Я успешно скомпилировал пример 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. Почему не функции имеют любые типы возврата, даже void ы?
  2. Когда порт Erlang отправляет данные на C, первый байт определяет функцию для вызова. Если байт содержит десятичное число 1, то вызывается foo(), если байт содержит десятичное число 2, то вызывается bar(). Если это не изменилось, этот протокол можно использовать для вызова до 255 различных функций C только с одним параметром. Это правильно?
  3. «Добавление индикатора длины будет выполняться автоматически через порт Erlang, но должно выполняться явно во внешней программе C». Что это значит? На какой строке кода это делается?
  4. Из учебника: «По умолчанию программа C должна считывать со стандартного ввода (дескриптор файла 0) и записывать в стандартный вывод (дескриптор файла 1)». Затем: «Обратите внимание, что stdin и stdout предназначены для буферизованного ввода/вывода и не должны использоваться для связи с Erlang!» Какая уловка здесь?
  5. Почему buf инициализирован до [100]?

ответ

8

Этот ответ также отрицается (я не Erlang или C программист, я просто случайно проходить через тот же материал)

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

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

+0

Ваше предположение о 'buf' верно. Это массив, то есть смежная область памяти, способная удерживать элементы 'n' указанного типа. В этом случае память выделяется в стеке. Другой способ выделения памяти - сделать указатель 'buf' указателем и выделить его в куче, используя' malloc' (но тогда вы должны убедиться, что «освободите» память самостоятельно, когда вы закончите с ней). –

+0

Относительно 4. Сначала в учебнике говорится, что программа C должна читать из stdin (дескриптор файла 0) и писать в stdout (дескриптор файла 1). Затем в учебнике говорится, что stdin/stdout не следует использовать для связи с Erlang ». Разве это не прямое противоречие? Они предоставляют примерную программу на языке С, которая использует stdin/stdout, а затем говорят, что ее не следует использовать, потому что stdin/stdout буферизуется. Думаю, я что-то пропустил здесь. – skanatek

+1

@MartinLee - О. Верно, я полагаю. Я полагал, что вы не должны использовать эти порты для обратной и четвертой связи между процессами Erlang и C (это то, что для C-узла есть) .Несмотря на то, что Erlang отправляет один запрос и ожидает один ответ int на вызов программы. Возможно, я неправильно понял или неправильно понял. – Inaimathi

Смежные вопросы