原创:Erlang gen_udp:open 流程详解

 

 

 

1

gen_udp:open(1000)

-module(gen_udp).

open(Port) ->

    open(Port, []).

 

gen_udp:open(1000, [])

-module(gen_udp).

open(Port, Opts) ->

    Mod = mod(Opts),

    {ok,UP} = Mod:getserv(Port),

    Mod:open(UP, Opts).

 

Mod = mod([]) à mod()

-module(gen_udp).

mod([{udp_module,Mod}|_]) ->

    Mod;

mod([inet|_]) ->

    inet_udp;

mod([inet6|_]) ->

    inet6_udp;

mod([_|Opts]) ->

    mod(Opts);

mod([]) ->

    mod().

 

Mod = mod() à inet_db:udp_module()

-module(inet_db).

udp_module() -> db_get(udp_module).

 

Mod = inet_db:udp_module() à inet_db:db_get(udp_module)

-module(inet_db).

db_get(Name) ->

    case ets:lookup(inet_db, Name) of

    [] -> undefined;

    [{_,Val}] -> Val

    end.

 

直接在Shell中读取ETS表,得出

Mod=inet_udp

 

1> ets:lookup(inet_db, udp_module).

[{udp_module,inet_udp}]

2>

 

{ok, UP} = inet_udp:getserv(1000)

 

-module(inet_udp).

getserv(Port) when is_integer(Port) -> {ok, Port};

 

UP = 1000

Inet_udp:open(1000, [])

-module(inet_udp).

open(Port, Opts) when Port >= 0, Port =< 16#ffff ->

    case inet:udp_options(

       [{port,Port}, {recbuf, ?RECBUF} | Opts],

       inet) of

    {error, Reason} -> exit(Reason);

    {ok, R} ->

        Fd       = R#udp_opts.fd,

        BAddr    = R#udp_opts.ifaddr,

        BPort    = R#udp_opts.port,

        SockOpts = R#udp_opts.opts,

        inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,?MODULE)

    end.

 

inet:udp_options([{port, 1000}, {recbuf, 8*1024} | []], inet)

-module(inet).

udp_options(Opts, Family) ->

    case udp_opt(Opts, #udp_opts { }, udp_options()) of

    {ok, R} ->

        {ok, R#udp_opts {

          ifaddr = translate_ip(R#udp_opts.ifaddr, Family)

        }};

    Error -> Error

    end.

 

udp_opt([{port, 1000}, {recbuf, 8*1024} ], #udp_opts { }, udp_options())

-module(inet).

udp_opt([Opt | Opts], R, As) ->

    case Opt of

    {ip,IP}     -> udp_opt(Opts, R#udp_opts { ifaddr = IP }, As);

    {ifaddr,IP} -> udp_opt(Opts, R#udp_opts { ifaddr = IP }, As);

    {port,P}    -> udp_opt(Opts, R#udp_opts { port = P }, As);

    {fd,Fd}     -> udp_opt(Opts, R#udp_opts { fd = Fd }, As);

    binary      -> udp_add(mode, binary, R, Opts, As);

    list        -> udp_add(mode, list, R, Opts, As);

    {udp_module,_} -> udp_opt(Opts, R, As);

    inet        -> udp_opt(Opts, R, As);

    inet6       -> udp_opt(Opts, R, As);

    {Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As);

    _ -> {error, badarg}

    end;

 

Opt = [{port, 1000}]

Opts = [{recbuf, 8*1024}]

 

udp_opt([{port, 1000}, {recbuf, 8*1024} ], #udp_opts { }, udp_options())

à

udp_opt([{recbuf, 8*1024}], R#udp_opts{port = 1000}, udp_options())

à

注意这里遍历使用R 作为选项值,

 

这是利用了记录(record)的复制特性,这是一个很好的编程习惯

 

 

 

 

udp_add(recbuf, 8*1024, R#udp_opts{port = 1000}, [],udp_options())

-module(inet).

udp_add(Name, Val, R, Opts, As) ->

    case add_opt(Name, Val, R#udp_opts.opts, As) of

    {ok, SOpts} ->

        udp_opt(Opts, R#udp_opts { opts = SOpts }, As);

    Error -> Error

end.

 

 

 

add_opt(recbuf, 8*1024, [],udp_options())

add_opt(Name, Val, Opts, As) ->

    case lists:member(Name, As) of

    true ->

        case prim_inet:is_sockopt_val(Name, Val) of

       true ->

           Opts1 = lists:keydelete(Name, 1, Opts),

           {ok, [{Name,Val} | Opts1]};

       false -> {error,badarg}

        end;

    false -> {error,badarg}

    end.

 

udp_opt([recbuf,8*1024], R#udp_opt{port=1000}, udp_options())

 

à

 

{ok, R#udp_opt{port=1000, opts = [{active,true},{recbuf=8*1024}]}, [tos, priority, reuseaddr, sndbuf, header, active, buffer, mode, deliver, broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop, add_membership,drop_membership, read_packets,raw]}

-module(inet).

udp_opt([], R, _SockOpts) ->

{ok, R}.

udp_options() ->

    [tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,

     deliver,

     broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop,

     add_membership, drop_membership, read_packets,raw].

 

R#udp_opt{port=1000, opts = {recbuf=8*1024}}

 

à

 

Fd = -1,

BAddr = any,

BPort = 1000,

SockOpts = [{active,true},{recbuf=8*1024}]

-module(inet).

{ok, R} ->

        Fd       = R#udp_opts.fd,

        BAddr    = R#udp_opts.ifaddr,

        BPort    = R#udp_opts.port,

        SockOpts = R#udp_opts.opts,

 

-record(udp_opts,

    {

     ifaddr = any,

     port   = 0,

     fd     = -1,

     opts   = [{active,true}]

     }).

 

inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,?MODULE)

 

à

 

inet:open(-1,any,1000, [{active,true},{recbuf=8*1024}],udp,inet,?MODULE)

 

à

 

Fd = -1,

Addr = any,

Port = 1000,

Opts = [{active,true},{recbuf=8*1024}],

Protocol = udp,

Family = inet

Module = ?MODULE

 

开始创建描述符

 

-module(inet).

open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->

    case prim_inet:open(Protocol, Family) of

    {ok,S} ->

        case prim_inet:setopts(S, Opts) of

       ok ->

           case if is_list(Addr) ->

               prim_inet:bind(S, add,

                     [case A of

                          {_,_} -> A;

                          _     -> {A,Port}

                      end || A <- Addr]);

               true ->

               prim_inet:bind(S, Addr, Port)

            end of

           {ok, _} ->

               inet_db:register_socket(S, Module),

               {ok,S};

           Error ->

               prim_inet:close(S),

               Error

           end;

       Error ->

           prim_inet:close(S),

           Error

        end;

    Error ->

        Error

    end;

 

prim_inet:open(udp, inet)

-module(prim_inet).

open(Protocol,   inet) -> open1(Protocol, ?INET_AF_INET);

 

-define(INET_AF_INET,         1).

 

prim_inet:open1(udp, 1)

-module(prim_inet).

open1(Protocol, Family) ->

    case open0(Protocol) of

    {ok, S} ->

        case ctl_cmd(S, ?INET_REQ_OPEN, [Family]) of

       {ok, _} ->

           {ok,S};

       Error ->

           close(S), Error

        end;

    Error -> Error

    end.

 

prim_inet:open0(udp)

-module(prim_inet).

open0(Protocol) ->

    try    erlang:open_port({spawn,protocol2drv(Protocol)}, [binary]) of

    Port -> {ok,Port}

    catch

    error:Reason -> {error,Reason}

end.

 

protocol2drv(udp) -> udp_inet;

 

open_port 实际上是一个内建的BIF函数,该函数的实现是在

 

erts"emulator"beam"erl_bif_port.c中,

 

其主要由两部分组成

 

open_port_2

 

 

open_port

 

open_port_2的参数宏实质是三个参数,Process*, Eterm, Eterm

 

 

erts"emulator"beam"erl_bif_port.c

 

 

BIF_RETTYPE open_port_2(BIF_ALIST_2)

{

   ……

}

 

static int

open_port(Process* p, Eterm name, Eterm settings, int *err_nump)

{

  ……

}

 

#define BIF_ALIST_2 Process* A__p, Eterm A_1, Eterm A_2

 

open_port2中,将参数透明传入给open_port

erts"emulator"beam"erl_bif_port.c

 

 

#define BIF_P       A__p

#define BIF_ARG_1 A_1

#define BIF_ARG_2 A_2

 

 

BIF_RETTYPE open_port_2(BIF_ALIST_2)

{

 

if ((port_num = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_num)) < 0)    

{

    ……

}

 

……

 

}

 

再来回顾一下erlang里面open_port的用法

 

@spec open_port(PortName, [Opt]) -> Port

 

PortName 对应第二个参数name

 

{spawn, Command}

{fd, In, Out}

 

Opt     对应第三个参数 settings

 

{packet, N}

{steam}

{line, Max}

 

erts"emulator"beam"erl_bif_port.c

 

 

static int

open_port(Process* p, Eterm name, Eterm settings, int *err_nump)

{

 

…….

 

}

 

 

 

open_port 根据 PortName的两种不同类型来开启不同的Driver

 

 

由于在 init_io时已经将 spawn_driver 置为 spawn_driver_entry,因此这里直接就相当于找到 spawn_driver_entry

 

 

void init_io(void)

{

    init_driver(&fd_driver, &fd_driver_entry, NULL);

    init_driver(&vanilla_driver, &vanilla_driver_entry, NULL);

    init_driver(&spawn_driver, &spawn_driver_entry, NULL);

}

erts"emulator"beam"erl_bif_port.c

 

 

static int

open_port(Process* p, Eterm name, Eterm settings, int *err_nump)

{

 

if (*tp == am_spawn)

{

    ……

    driver = &spawn_driver;

}

else if (*tp == am_fd)

{

   ……

  driver = &fd_driver;

}

 

port_num = erts_open_driver(driver, p->id, name_buf, &opts, err_nump);

 

}

 

 

 

首先找到名为udp_inet的真正驱动程序,

 

然后回调驱动注册的start()函数

erts"emulator"beam"io.c

 

int

erts_open_driver(erts_driver_t* driver,   /* Pointer to driver. */

        Eterm pid,       /* Current process. */

        char* name,      /* Driver name. */

        SysDriverOpts* opts, /* Options. */

        int *error_number_ptr) /* errno in case -2 is returned */

 

{

……

if (driver == &spawn_driver)

{

    ……

    for (d = driver_list; d; d = d->next)

{

           if (strcmp(d->name, name) == 0 && erts_ddll_driver_ok(d->handle))

{

               driver = d;

           break;

        }

    }

    ……

}

……

 

if (driver->start)

{

    ……

   drv_data = (*driver->start)((ErlDrvPort)(port_ix), name, opts);

    ……

}

……

}

 

 

erlang:open_port({spawn, udp_inet}, [binary])

 

回调udp驱动注册的创建回调函数udp_inet_start()

erts"emulator"driver"common"inet_driver.c

 

static ErlDrvData udp_inet_start(ErlDrvPort port, char *args)

{

    return packet_inet_start(port, args, IPPROTO_UDP);

}

 

udp_inet_start()

 

à

 

packet_inet_start()

erts"emulator"driver"common"inet_driver.c

 

static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol)

{

    /* "inet_start" returns "ErlDrvData", but in fact it is "inet_descriptor*",

       so we can preserve it as "ErlDrvData":

    */

    ErlDrvData     drvd = inet_start(port, sizeof(udp_descriptor),

                    protocol);

    udp_descriptor* desc = (udp_descriptor*) drvd;

 

    if (desc == NULL)

    return ERL_DRV_ERROR_ERRNO;

 

    desc->read_packets = INET_PACKET_POLL;

    return drvd;

}

 

inet_start()中创建相应协议的端口,返回

erts"emulator"driver"common"inet_driver.c

 

 

static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol)

{

   ……

  

}

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值