C 和 Erlang : Erlang Port example

标签 c erlang stdout stdin erlang-ports

免责声明:问题的作者对 Erlang 有一般的了解,对 C 有一定的了解。

我正在阅读 Interoperability Tutorial User Guide现在。我已经成功编译了 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. 为什么函数没有任何返回类型,即使是 void
  2. 当Erlang端口向C发送数据时,第一个字节决定了要调用的函数。如果字节包含十进制数 1,则调用 foo(),如果字节包含十进制数 2,则调用 bar()。如果不做任何更改,此协议(protocol)可用于调用多达 255 个不同的 C 函数,每个函数只有 1 个参数。对吗?
  3. “添加长度指示器将由 Erlang 端口自动完成,但必须在外部 C 程序中显式完成”。那是什么意思?在哪一行代码上完成?
  4. 来自教程: “默认情况下,C 程序应从标准输入(文件描述符 0)读取并写入标准输出(文件描述符 1)。” 然后:“请注意,stdin 和 stdout 用于缓冲输入/输出,不应用于与 Erlang 的通信!”这里有什么问题?
  5. 为什么 buf 被初始化为 [100]

最佳答案

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

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

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

0 - b) 我认为 len 已在上面充分介绍。 li 是用于为写入函数生成那个两字节 header 的变量的名称(我无法带您逐步完成位移操作,但最终结果是 len 在发送的前两个字节中表示)。 i 是一个中间变量,其主要目的似乎是确保 writeread 不返回错误(如果返回错误,那错误代码作为 read_exact/write_exact 的结果返回)。 wrotegot 跟踪已写入/读取了多少字节,包含循环在它变得大于 len 之前退出。

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

2 - 没错,但端口协议(protocol)的要点是您可以更改它以发送不同的参数(如果您发送任意参数,它会只使用 C Node 方法而不是端口可能是一个更好的主意)。例如,我 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) 中;这是一个 header ,它告诉/找出消息的其余部分将有多长(是的,这意味着它只能是有限长度,并且该长度必须可以用两个字节表示)。

4 - 我不完全确定为什么这会成为一个陷阱。愿意详细说明吗?

5 - buf 是一个字节数组,必须明确界定(我认为是出于内存管理目的)。我在这里将“100”读作“一个大于我们计划容纳的最大消息大小的数字”。实际选择的数字似乎是任意的,似乎任何 4 或更高的数字都可以,但我可以在这一点上得到纠正。

关于C 和 Erlang : Erlang Port example,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10488388/

相关文章:

审查c中的单词

javascript - 如何将公历天数转换为公历日期

c - 如何在 c 中使用 execl() 函数在文件中打印 stderr 消息

从 c 调用汇编函数

c - 链表程序崩溃

ruby-on-rails - 将 CouchDB 作为 Rails 应用程序的一部分分发?

python - 如何在 python 2.x 和 3.x 中直接打印到文本文件?

Python - 在先前将标准输出重定向到文件后将其重置为正常

c - 用 C 语言编写一个程序,提示用户输入两个日期,然后指示哪个日期在日历上较早

windows - 在 Windows 服务器上设置 RabbitMQ 集群