|
|
|
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) { ……
} |
|
|
|
|
|
|
|
|
|
|
|
|
原创:Erlang gen_udp:open 流程详解
最新推荐文章于 2024-01-11 11:04:23 发布