C 和 Erlang:Erlang 端口示例

Posted

技术标签:

【中文标题】C 和 Erlang:Erlang 端口示例【英文标题】:C and Erlang: Erlang Port example 【发布时间】:2012-05-16 07:59:47 【问题描述】:

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

我现在正在阅读Interoperability Tutorial User Guide。我已经成功编译了complex.c 示例,它可以与 Erlang 端口一起使用,没有任何问题。

但是,我想了解实际的 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 变量的实际用途是什么?

还有一些小问题:

    为什么函数没有任何返回类型,甚至voids? 当 Erlang 端口向 C 发送数据时,第一个字节确定要调用的函数。如果字节包含十进制 1,则调用 foo(),如果字节包含十进制 2,则调用 bar()。如果不进行任何更改,该协议可用于调用多达 255 个不同的 C 函数,每个函数只有 1 个参数。对吗? “添加长度指示器将由 Erlang 端口自动完成,但必须在外部 C 程序中显式完成”。那是什么意思?它是在哪一行代码上完成的? 来自教程: “默认情况下,C 程序应该从标准输入(文件描述符 0)读取并写入标准输出(文件描述符 1)。” 然后:“请注意,stdin 和 stdout 用于缓冲输入/输出,不应用于与 Erlang 的通信!”这里有什么问题? 为什么buf被初始化为[100]

【问题讨论】:

【参考方案1】:

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

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

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

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

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

2 - 没错,但是端口协议的重点是您可以更改它以发送不同的参数(如果您要发送 任意 参数,它会只使用C Node 方法而不是端口可能是一个更好的主意)。例如,我在最近的一篇文章中modified it subtly 发送了一个字符串,因为我只有一个要在 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 或更高的数字都可以,但我可以在这一点上得到纠正。

【讨论】:

您对buf 的假设是正确的。它是一个数组,即能够保存指定类型的n 元素的连续内存区域。在这种情况下,分配在堆栈上的内存。分配内存的另一种方法是使buf 成为指针并使用malloc 在堆上分配它(但是当你完成它时,你必须确保自己free 内存)。 @MartinLee - 哦。是的,我想。我的意思是你不应该使用这些端口在 Erlang 和 C 进程之间进行来回通信(这就是 C 节点的用途)。我们不是; Erlang 正在发送单个请求,并且每次调用程序时都期望单个 int 响应。我想我可能误解或误读了。 @MartinLee - 我们将如何计算len?我想您可以更改协议,使其使用一个字节并让它按字面意思解释为无符号字节,但这会使您的最大消息长度为 255。2-byte 长度可能是任意的;易于存储与消息长度之间的某种特殊平衡。 从所涉及的位考虑。 0,2 -> [00000000], [00000010]len 是一个整数,所以实际上是 4 个字节; [00000000:00000000:00000000:00000000]。执行[00000000] &lt;&lt; 8 会将其向左移动 8 位(在这种情况下,它没有区别,因为第一位是 0),得到[00000000:00000000]。执行([00000000] &lt;&lt; 8) | [00000010] 会留下 [00000000:00000010],这是“短整数 2”的位表示。这被分配给 len 并剩下两个字节(因此您可以在技术上将 len 声明为 short integer 而不会产生不良影响)。 那是我自己对“非 C 程序员”的理解,所以请持保留态度。观看this 也可能有所帮助。这是一个约 5 分钟的片段,其中他专门谈论短裤。 ~20 minute mark 上还有大约 10 分钟的相当有用的示例。

以上是关于C 和 Erlang:Erlang 端口示例的主要内容,如果未能解决你的问题,请参考以下文章

Erlang 标准模块与使用手册

Erlang - C 和 Erlang

erlang字符串动态解析成为Erlang数据结构(去掉“”)

Erlang:从命令行调用 erl -eval 永远不会退出

Erlang:当你在 erl 中 f() 一个 Pid 时会发生啥?

Erl 中的记录(Erlang 问题)