8

免责声明:该问题的作者对 Erlang 有一个平均的了解,对 C 有基本的了解。

我现在正在阅读互操作性教程用户指南。我已经成功编译了这个complex.c例子,它可以与 Erlang Port 一起使用,没有任何问题。

但是,我想了解实际的 C 代码是如何工作的。我一般理解它:在示例中,它从标准输入中读取 2 个字节并检查第一个字节。根据第一个字节,它调用foobar函数。这是我目前对它的理解的极限。

因此,如果我们同时采用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 函数,每个函数只有 1 个参数。那正确吗?
  3. “添加长度指示器将由 Erlang 端口自动完成,但必须在外部 C 程序中显式完成”。这意味着什么?它是在哪一行代码上完成的?
  4. 来自教程:“默认情况下,C 程序应从标准输入(文件描述符 0)读取并写入标准输出(文件描述符 1)。” 然后:“请注意,stdin 和 stdout 用于缓冲输入/输出,不应用于与 Erlang 的通信!” 这里有什么问题?
  5. 为什么buf初始化为[100]
4

1 回答 1

6

这个答案同样被否认(我不是 Erlang 或 C 程序员,我只是碰巧正在经历相同的材料)

您的初始模型有点偏离。代码实际工作的方式是从 读取前两个字节stdin,假设它表示实际消息的长度,然后从 读取更多字节stdin。在这种特定情况下,实际消息总是两个字节(一个对应于函数的数字和一个要传递给它的整数参数)。

0 - a) read_exact从 读取len字节stdin,首先read_cmd使用read_exact以确定它应该读取多少字节(前两个字节表示的数字,或者如果可用的字节少于两个,则没有),然后读取那么多字节。write_exactlen字节写入stdoutwrite_cmd用于write_exact输出两个字节长度的标头,后跟一条适当长度的消息(希望如此)。

0 - b)我认为len上面已经充分涵盖了。li是用于为写入函数生成该两字节标头的变量的名称(我不能逐步带您完成移位操作,但最终结果是len在发送的前两个字节中表示)。i是一个中间变量,其主要目的似乎是确保不返回错误(如果返回,则返回错误代码作为 / 的write结果)。并跟踪已写入/读取的字节数,包含循环在大于.readread_exactwrite_exactwrotegotlen

1 -我实际上不确定。我使用的版本是 type int,但在其他方面是相同的。我是从Programming Erlang的第 12 章中得到的,而不是您链接的指南。

2 -这是正确的,但是端口协议的重点是您可以更改它以发送不同的参数(如果您发送任意参数,那么使用C 节点方法而不是端口可能会更好)。例如,我在最近的一篇文章中巧妙地修改了它,以便它发送单个字符串,因为我只有一个要在 C 端调用的函数,从而无需指定函数。我还应该提到,如果您的系统需要调用超过 255 个用 C 编写的不同操作,您可能需要重新考虑它的结构(或者只使用整个 9 个并用 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 或更高的数字都可以,但我可以在这一点上得到纠正。

于 2012-05-08T02:00:52.673 回答