前面聊了普通端口,今天聊下链入式驱动端口,以及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. jQuery中$this和$(this)的区别

    要写一个点击弹窗任意地方,关闭弹窗.点击事件写标签在元素上 onclick =  closepop(this),这时候很容易搞不清楚怎么去获取当前元素 function closepop(e){ va ...

  2. mongo 3.4分片集群系列之二:搭建分片集群--哈希分片

    这个系列大致想跟大家分享以下篇章: 1.mongo 3.4分片集群系列之一:浅谈分片集群 2.mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3.mongo 3.4分片集群系列之三:搭建 ...

  3. Angular——$http

    基本介绍 $http用于向服务端发起异步请求,同时还支持多种快捷方式如$http.get().$http.post().$http.jsonp.$hhtp也是属于内置服务的一种,这里特意提出来写一篇用 ...

  4. [Windows Server 2012] 初识Windows Server 2012

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:初次见识Win ...

  5. MySql学习笔记(三) —— 聚集函数的使用

    1.AVG() 求平均数 select avg(prod_price) as avg_price from products; --返回商品价格的平均值 ; --返回生产商id为1003的商品价格平均 ...

  6. 当From窗体中数据变化时,使用代码获取数据库中的数据然后加入combobox中并且从数据库中取得最后的结果

    private void FormLug_Load(object sender, EventArgs e) { FieldListLug.Clear();//字段清除 DI = double.Pars ...

  7. 12Java Bean

     Java Bean JavaBean是一种组件体系结构.实际上,JavaBean就是一个Java类,这个类可以重复地使用.我们可以把JavaBean看成是一个黑盒子,即只需要知道其功能而不必管其内部 ...

  8. centos 7桌面和命令行转行

    CentOS7图形界面与命令行界面(终端)切换(1)CentOS7 在图形界面进入dos界面 :ctrl+alt+F6 dos界面进入图形界面:ctrl+alt+F2 本机用的这个命令: (2)Cen ...

  9. 登录deepin 15.9后不显示任务栏,无法操作

    一直觉得在Linux下编程很酷,所以决定装个Deepin试试,安装很顺利,然后搭建了开发环境,写了一个简单程序,觉得挺不错的. 哪知第二天一开机,登录后找不到任务栏了,做不了啥操作,走接傻眼了,直觉以 ...

  10. 配置Struts2后运行jsp出现404的解决方法

    更新:善用控制台查看错误信息 --------------------------------------------- 原因:Java Build Path没有导入正确的jar包或者导入了但没有把相 ...