前面聊了普通端口,今天聊下链入式驱动端口,以及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. tomcat日志详释

    1.tomcat的日志分类: 一是运行中的日志,它主要记录运行的一些信息,尤其是一些异常错误日志信息 . 二是访问日志信息,它记录的访问的时间,IP ,访问的资料等相关信息. 2.tomcat的日志目 ...

  2. ci框架中model简单的mysql操作

    <?php class SingerModel extends CI_Model { function SingerModel() { //会将数据库对象赋值给CI_Controller的db属 ...

  3. 【译】x86程序员手册30-8.2 I/O指令

    8.2 I/O Instructions I/O指令 The I/O instructions of the 80386 provide access to the processor's I/O p ...

  4. dwarfdump --arch=arm64 --lookup

    解析友盟错误信息重要指令: dwarfdump --arch=arm64 --lookup 0x1001edbc4 /Users/zhoujunbo/Library/Developer/Xcode/A ...

  5. 梦想3D控件 2018.7.26更新

    下载地址: http://www.mxdraw.com/ndetail_108.html 1.  编写所有接口函数使用的CHM文档 2.  增加交互绘制功能 3.  增加案例弧形窗建模案例 4.  增 ...

  6. RabbitMQ系列(五)--高级特性

    在上一篇文章讲解MQ消息可靠性投递和幂等性中有提到confirm机制的重要性,现在更相信的说明一下 一.Confirm机制 Confirm就是消息确认,当Producer发送消息,如果Broker收到 ...

  7. php总结回顾

    做人不能一直埋着头往前跑,还要偶尔停下来看下来时的路.所以今天就来回顾下之前的吧 下面依次介绍 [一]TP加载流程 ①应用入口文件index.php→②tp公共入口文件ThinkPHP.php→③核心 ...

  8. 07Microsoft SQL Server View

    Microsoft SQL Server View 1.视图 视图是一个虚拟的表,是表中的数据经过某种筛选后的显示方式,视图由预定义的查询select语句组成. 2.查看视图信息 --查询系统所有视图 ...

  9. linux 下mysql无法启动 mysql.sock

    在公司装的一键安装的lnmp环境,启动mysql时候发现mysql.sock不存在, 然后我进行查找  最后在  /usr/local/mysql/bin/mysql_safe  重新启动下 然后启动 ...

  10. Cash Machine POJ - 1276

    解法 多重背包板子题 多重背包板子 如果上限的体积大于了给定的体积那么套完全背包 否则二进制优化成01背包 代码 #include <iostream> #include <cstr ...