【Erlang新手成长日记】Erlang端口与外部C程序交互
参考文档:
《Programming Erlang》,第12章:Interfacing Techniques
创建C源文件:
hello.c 具体函数的实现

int twice(int arg) { return (arg * arg); } int sum(int arg1, int arg2) { return (arg1 + arg2); }
hello_driver.c 驱动程序运行

#include <stdio.h> #define BUFFER_SIZE 100 typedef unsigned char byte; //8 bit int read_cmd(byte *buffer); int write_cmd(byte *buffer, int length); int main() { int function_number; //函数号 int arg1, arg2; //参数 int result; //结果 byte buffer[BUFFER_SIZE]; //缓冲区 while(read_cmd(buffer) > 0) { function_number = buffer[0]; switch(function_number) { case 1: //执行函数 twice arg1 = buffer[1]; result = twice(arg1); break; case 2: //执行函数 sum arg1 = buffer[1]; arg2 = buffer[2]; result = sum(arg1, arg2); break; default: // error break; } buffer[0] = result; write_cmd(buffer, 1); } return 0; }
erl_communication.c 与Erlang程序交互

1 #include <unistd.h> 2 3 typedef unsigned char byte; 4 5 int read_cmd(byte *buffer); 6 int write_cmd(byte *buffer, int length); 7 int read_exact(byte *buffer, int length); 8 int read_exact(byte *buffer, int length); 9 10 int read_cmd(byte *buffer) { 11 int length; //int 长度 16 bit 12 13 if(read_exact(buffer, 2) != 2) { 14 return -1; 15 } 16 length = (buffer[0] << 8) | buffer[1]; 17 18 return read_exact(buffer, length); 19 } 20 21 int write_cmd(byte *buffer, int length) { 22 byte li; //unsigned char 长度 8 bit 23 24 li = (length >> 8) & 0xff; 25 write_exact(&li, 1); 26 li = length & 0xff; 27 write_exact(&li, 1); 28 29 return write_exact(buffer, length); 30 } 31 32 int read_exact(byte *buffer, int length) { 33 int i, got = 0; 34 35 do { 36 /* 37 * read函数定义:ssize_t read(int fildes, void *buf, size_t nbyte); 38 * read函数描述:The read() function attempts to read nbyte bytes from the file associated with the open file descriptor, fildes, into the buffer pointed to by buf. 39 * 备注:fildes值0,标准输入 40 */ 41 if((i = read(0, (buffer + got), (length - got))) <= 0) { 42 return i; 43 } 44 got += i; 45 } while(got < length); 46 47 return length; 48 } 49 50 int write_exact(byte *buffer, int length) { 51 int i, wrote = 0; 52 53 do { 54 /* 55 * write函数定义:ssize_t write(int fildes, const void *buf, size_t nbyte); 56 * write函数描述:The write() function attempts to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes. 57 * 备注:fildes值1,标准输出 58 */ 59 if((i = write(1, (buffer + wrote), (length - wrote)))) { 60 return i; 61 } 62 wrote += i; 63 } while(wrote < length); 64 65 return length; 66 }
创建Erlang源文件
hello.erl 创建进程,打开与C生成的可执行文件之间的端口,通过消息传递进行交互
Port = open_port(PortName, PortSettings)
open_port/2:http://www.erlang.org/doc/man/erlang.html#open_port-2
This returns a port. The following messages can be sent to a port:
Port ! {PidC, {command, Data}}
Send Data (an IO List) to the port.
Port !{PidC, {connect, Pid1}}
Change the PID of the connected process from PidC to Pid1.
Port ! {PidC, close}
Close the port.

1 -module(hello). 2 3 -export([start/0, stop/0]). 4 -export([twice/1, sum/2]). 5 6 -define(PORTNAME, ?MODULE). 7 8 start() -> 9 Fun = fun() -> 10 register(?PORTNAME, self()), 11 process_flag(trap_exit, true), 12 Port = open_port({spawn, "./hello"}, [{packet, 2}]), 13 loop(Port) 14 end, 15 spawn(Fun). 16 17 stop() -> 18 ?PORTNAME ! stop. 19 20 twice(X) -> call_port({twice, X}). 21 sum(X, Y) -> call_port({sum, X, Y}). 22 23 call_port(Message) -> 24 ?PORTNAME ! {call, self(), Message}, 25 receive 26 {?PORTNAME, Result} -> 27 Result 28 end. 29 30 loop(Port) -> 31 receive 32 {call, Caller, Message} -> 33 Port ! {self(), {command, encode(Message)}}, 34 receive 35 {Port, {data, Data}} -> 36 Caller ! {?PORTNAME, decode(Data)} 37 end, 38 loop(Port); 39 stop -> 40 Port ! {self(), close}, 41 receive 42 {Port, closed} -> 43 exit(normal) 44 end; 45 {'EXIT', Port, Reason} -> 46 exit({port_terminated, Reason}) 47 end. 48 49 encode({twice, X}) -> 50 [1, X]; 51 encode({sum, X, Y}) -> 52 [2, X, Y]. 53 54 decode([Int]) -> 55 Int.
编译C源文件,生成可执行文件
gcc -o hello hello.c hello_driver.c erl_communication.c
编译Erlang源文件,生成.beam文件
erlc hello.erl
Android 开发讨论群:84778336
iOS 开发讨论群:82873648

本作品采用知识共享署名-非商业性使用 3.0 许可协议进行许可。
转载请署名李震(博客地址:http://www.cnblogs.com/dyingbleed/),且不得用于商业目的。
博客园博客已停止更新,博客地址:dyingbleed.com
iOS 开发讨论群:82873648

本作品采用知识共享署名-非商业性使用 3.0 许可协议进行许可。
转载请署名李震(博客地址:http://www.cnblogs.com/dyingbleed/),且不得用于商业目的。
博客园博客已停止更新,博客地址:dyingbleed.com
分类:
Erlang
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?