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 个字节并检查第一个字节。根据第一个字节,它调用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 函数,每个函数只有 1 个参数。对吗?
“添加长度指示器将由 Erlang 端口自动完成,但必须在外部 C 程序中显式完成”。那是什么意思?它是在哪一行代码上完成的?
来自教程:
“默认情况下,C 程序应该从标准输入(文件描述符 0)读取并写入标准输出(文件描述符 1)。”
然后:“请注意,stdin 和 stdout 用于缓冲输入/输出,不应用于与 Erlang 的通信!”这里有什么问题?
为什么buf
被初始化为[100]
?
【问题讨论】:
【参考方案1】:这个答案同样被拒绝(我不是 Erlang 或 C 程序员,我只是碰巧正在阅读相同的材料)
您的初始模型有点偏离。代码实际工作的方式是从stdin
读取前两个字节,假设它表示实际消息的长度,然后从stdin
读取更多字节。在这种特定情况下,实际消息总是两个字节(一个对应于函数的数字和一个要传递给它的整数参数)。
0 - a) read_exact
从stdin
读取len
字节,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
类型,但其他方面相同。我是从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] << 8
会将其向左移动 8 位(在这种情况下,它没有区别,因为第一位是 0),得到[00000000:00000000]
。执行([00000000] << 8) | [00000010]
会留下 [00000000:00000010],这是“短整数 2”的位表示。这被分配给 len
并剩下两个字节(因此您可以在技术上将 len
声明为 short integer
而不会产生不良影响)。
那是我自己对“非 C 程序员”的理解,所以请持保留态度。观看this 也可能有所帮助。这是一个约 5 分钟的片段,其中他专门谈论短裤。 ~20 minute mark 上还有大约 10 分钟的相当有用的示例。以上是关于C 和 Erlang:Erlang 端口示例的主要内容,如果未能解决你的问题,请参考以下文章
erlang字符串动态解析成为Erlang数据结构(去掉“”)
Erlang:从命令行调用 erl -eval 永远不会退出