前面聊了普通端口,今天聊下链入式驱动端口,以及NIFs。

  • 链入式驱动端口

  

  如上图所示,链入式驱动端口与Erlang虚拟机存在于同一个OS进程中。

  在Erlang这边与普通端口类似,所有与链入式驱动端口的通信,也是通过一个Erlang的连接进程进行的,终止这个进程将同时终止端口。在端口创建之前,驱动需要先载入运行时(驱动以动态库方式提供),可以通过erl_dll:load_driver/1函数。然后就与普通端口一样,使用open_port/2函数打开。  

-module(complex5).
-export([start/, stop/, init/]).
-export([foo/, bar/]). start(SharedLib) ->
case erl_ddll:load_driver(".", SharedLib) of  %% 载入驱动
  ok -> ok;
   {error, already_loaded} -> ok;
  _ -> exit({error, could_not_load_driver})
end,
spawn(?MODULE, init, [SharedLib]). init(SharedLib) ->
register(complex, self()),
Port = open_port({spawn, SharedLib}, []),
loop(Port). stop() ->
complex ! stop. foo(X) ->
call_port({foo, X}).
bar(Y) ->
call_port({bar, Y}). call_port(Msg) ->
complex ! {call, self(), Msg},
receive
{complex, Result} ->
Result
end. loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
{Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, Reason} ->
io:format("~p ~n", [Reason]),
exit(port_terminated)
end. encode({foo, X}) -> [, X];
encode({bar, Y}) -> [, Y]. decode([Int]) -> Int.

  在C这边,首先包含erl_driver.h,需要使用里面的driver structure将C函数暴露给端口,这些函数用于接收和发送数据。端口发送过来的数据以函数参数的形式提供,而从C发送数据回端口则需要使用driver_output函数。端口由于可能被多个Erlang进程打开,所以最好不要使用全局变量,不然函数不可重入。应当使用driver_alloc,driver_free来分配每个端口独有的数据。最后我们需要把模块编译成动态库。

/* port_driver.c */

#include <stdio.h>
#include "erl_driver.h" typedef struct {
ErlDrvPort port;
} example_data; static ErlDrvData example_drv_start(ErlDrvPort port, char *buff)
{
example_data* d = (example_data*)driver_alloc(sizeof(example_data));
d->port = port;  //start 回调,保存端口在独立的数据中,以后的回调可以访问
return (ErlDrvData)d;
} static void example_drv_stop(ErlDrvData handle)
{
driver_free((char*)handle);
} static void example_drv_output(ErlDrvData handle, char *buff,
ErlDrvSizeT bufflen)
{
example_data* d = (example_data*)handle;
char fn = buff[], arg = buff[], res;
if (fn == ) {
res = foo(arg);
} else if (fn == ) {
res = bar(arg);
}
driver_output(d->port, &res, );//输出结果
} ErlDrvEntry example_driver_entry = {  //注册端口回调
NULL, /* F_PTR init, called when driver is loaded */
example_drv_start, /* L_PTR start, called when port is opened */
example_drv_stop, /* F_PTR stop, called when port is closed */
example_drv_output, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"example_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* void *handle, Reserved by VM */
NULL, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL, /* F_PTR outputv, reserved */
NULL, /* F_PTR ready_async, only for async drivers */
NULL, /* F_PTR flush, called when port is about
to be closed, but there is data in driver
queue */
NULL, /* F_PTR call, much like control, sync call
to driver */
NULL, /* F_PTR event, called when an event selected
by driver_event() occurs. */
ERL_DRV_EXTENDED_MARKER, /* int extended marker, Should always be
set to indicate driver versioning */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* int major_version, should always be
set to this value */
ERL_DRV_EXTENDED_MINOR_VERSION, /* int minor_version, should always be
set to this value */
, /* int driver_flags, see documentation */
NULL, /* void *handle2, reserved for VM use */
NULL, /* F_PTR process_exit, called when a
monitored process dies */
NULL /* F_PTR stop_select, called to close an
event object */
}; DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
return &example_driver_entry;  //初始化端口回调
}
  • NIFs

  NIFs是一种更简单,高效的通信方法,特别适合一些同步调用的函数,这些函数只是简单的计算并返回结果,并且没有副作用。它也需要编译成动态库,加载入运行时中。在Erlang这边,我们也需要一个Erlang模块来引用它,首先通过这个模块加载NIFs库(erlang:load_nif/2),然后在Erlang这边实现NIFs里同名的函数作为stub implementations,这些函数实现很简单,只是抛出异常。当NIFs中没有这个函数时,这些函数将作为替代被调用。

-module(complex6).
-export([foo/1, bar/1]).
-on_load(init/0).  %%在模块被加载时调用init/0函数,如果过init不是返回的ok,模块将会加载失败 init() ->
ok = erlang:load_nif("./complex6_nif", 0). foo(_X) ->
exit(nif_library_not_loaded).
bar(_Y) ->
exit(nif_library_not_loaded).

  在C这边,我们通过ERL_NIF_INIT宏将函数暴露出去。函数的参数在C这边通过一个ERL_NIF_TERM类型的数组表示。第Nth参数为数组里Nth-1元素。ErlNifEnv指针里包含了许多Erlang进程的信息,我们通过它调用enif_xx函数与Erlang通信。

#include "erl_nif.h"

extern int foo(int x);
extern int bar(int y); static ERL_NIF_TERM foo_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int x, ret;
if (!enif_get_int(env, argv[], &x)) {
return enif_make_badarg(env);
}
ret = foo(x);
return enif_make_int(env, ret);
} static ERL_NIF_TERM bar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int y, ret;
if (!enif_get_int(env, argv[], &y)) {
return enif_make_badarg(env);
}
ret = bar(y);
return enif_make_int(env, ret);
} static ErlNifFunc nif_funcs[] = {
{"foo", , foo_nif},
{"bar", , bar_nif}
}; ERL_NIF_INIT(complex6, nif_funcs, NULL, NULL, NULL, NULL)

  链入式驱动端口,和NIFs就聊到这,后面再写下C Nodes。

Erlang下与其他程序和语言的通信机制(2)的更多相关文章

  1. Erlang下与其他程序和语言的通信机制(1)

    在Erlang运行时中,提供了几种机制来实现与其它程序或者语言的通信.一种为分布式Erlang,一种为端口,其中端口分为普通端口和链入式驱动端口,还有后面引入的NIFs. 分布式Erlang:一个Er ...

  2. Erlang下与其他程序和语言的通信机制(3)

    这部分主要聊C Nodes.首先我们需要了解下Erlang的分布式系统: 分布式Erlang: 分布式Erlang是由一组相互通信的Erlang运行时组成,其中每一个运行时称为一个Node.不同Nod ...

  3. netlink---Linux下基于socket的内核和上层通信机制 (转)

    需要在linux网卡 驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文COPY下必要的数据交给用户 态来完成(因为过于复杂的报文消耗 ...

  4. Linux下who命令之C语言实现

    Linux下who命令之C语言实现 Step1:前期准备 首先要有一个清楚的认识:linux中一切皆文件 实现who命令,who命令也是Linux中的一个文件,那我们怎么找到它呢?我们可以" ...

  5. JAVA设置环境变量和在DOS下运行java程序

    在学校实训的这几天,老师带着我们开始深入的复习java.这是第一天的内容哦 对于“JAVA设置环境变量和在DOS下运行java程序”,许多初学者是陌生的,但了解这个却对后期的学习很重要. http:/ ...

  6. erlang下lists模块sort(排序)方法源码解析(一)

    排序算法一直是各种语言最简单也是最复杂的算法,例如十大经典排序算法(动图演示)里面讲的那样 第一次看lists的sort方法的时候,蒙了,几百行的代码,我心想要这么复杂么(因为C语言的冒泡排序我记得不 ...

  7. 微信小程序开发语言的选择

    微信使用的开发语言和文件很「特殊」. 小程序所使用的程序文件类型大致分为以下几种: ①WXML(WeiXin Mark Language,微信标记语言) ②WXSS(WeiXin Style Shee ...

  8. Linux下的 sniff-andthen-spoof程序编写

    Linux下的 sniff-andthen-spoof程序编写 一.任务描述 在本任务中,您将结合嗅探和欺骗技术来实现以下嗅探然后欺骗程序.你需要两台机器在同一个局域网.从机器A ping IP_X, ...

  9. VB.net 2010下关联与程序图标设置

    '*************************************************************************'**模 块 名:VB.net 2010下关联与程序 ...

随机推荐

  1. js操作元素透明度以及浏览器兼容性

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  2. python 正则表达式操作

    正则表达式操作  本模块提供了类似于Perl的正则表达式匹配操作.要匹配的模式和字符串可以是Unicode字符串以及8位字符串. 正则表达式使用反斜杠字符('\')来表示特殊的形式或者来允许使用特殊的 ...

  3. NodeJS —— 自定义流的实现

    概述 常见的自定义流有四种,Readable(可读流).Writable(可写流).Duplex(双工流)和 Transform(转换流),常见的自定义流应用有 HTTP 请求.响应,crypto 加 ...

  4. magento 购物车 首页 显示

    如何将购物车显示在你的首页 1.复制代码:<!--new block -->                <block type="checkout/cart_sideb ...

  5. (转) 淘淘商城系列——Redis集群的搭建

    http://blog.csdn.net/yerenyuan_pku/article/details/72860432 本文我将带领大家如何搭建Redis集群.首先说一下,为何要搭建Redis集群.R ...

  6. CAD把当前图形保为一个jpg文件(com接口Delphi语言)

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 procedure TForm2.Button1Click(Sender: TObje ...

  7. adb 命令收藏学习地址

    adb 命令相关的网页https://www.cnblogs.com/medsonk/p/8334847.htmlhttps://www.cnblogs.com/medsonk/p/6959658.h ...

  8. ZOJ - 3981 - Balloon Robot (思维)

    参考自:https://blog.csdn.net/qq_36553623/article/details/78445558 题意: 第一行三个数字n, m, q表示有m个座位围成一个环,n个队伍,q ...

  9. 简述systemd的新特性及unit常见类型分析、使用systemd管理编译安装的nginx

    1. systemd新特性 并行处理(同时启动)所有服务. 基于依赖关系定义的服务控制逻辑 系统状态快照 按需激活进程,只有第一次被访问时才会真正启动: 2. systemd的常见unit类型 Ser ...

  10. buf.writeInt8()函数详解

    buf.writeInt8(value, offset[, noAssert]) value {Number} 需要被写入到 Buffer 的字节 offset {Number} 0 <= of ...