今天读到
褚霸博客里的
gen_tcp:send的深度解刨和使用指南(初稿)时,顺着霸爷的思路,跟着
lib/kernel/src/gen_tcp.erl
send(S, Packet) when is_port(S) ->
case inet_db:lookup_socket(S) of
{ok, Mod} ->
Mod:send(S, Packet);
Error ->
Error
end.
lib/kernel/src/inet_tcp.erl
%%
%% Send data on a socket
%%
send(Socket, Packet, Opts) -> prim_inet:send(Socket, Packet, Opts).
send(Socket, Packet) -> prim_inet:send(Socket, Packet, []).
erts/preloaded/src/prim_inet.erl
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% SEND(insock(), Data) -> ok | {error, Reason}
%%
%% send Data on the socket (io-list)
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This is a generic "port_command" interface used by TCP, UDP, SCTP, depending
%% on the driver it is mapped to, and the "Data". It actually sends out data,--
%% NOT delegating this task to any back-end. For SCTP, this function MUST NOT
%% be called directly -- use "sendmsg" instead:
%%
send(S, Data, OptList) when is_port(S), is_list(OptList) ->
?DBG_FORMAT("prim_inet:send(~p, ~p)~n", [S,Data]),
try erlang:port_command(S, Data, OptList) of
false -> % Port busy and nosuspend option passed
?DBG_FORMAT("prim_inet:send() -> {error,busy}~n", []),
{error,busy};
true ->
receive
{inet_reply,S,Status} ->
?DBG_FORMAT("prim_inet:send() -> ~p~n", [Status]),
Status
end
catch
error:_Error ->
?DBG_FORMAT("prim_inet:send() -> {error,einval}~n", []),
{error,einval}
end.
看到了使用erlang:port_command/3来向ERTS发送数据,根据erlang:port_command的文档的说明,
引用
port_command(Port, Data, OptionList) -> boolean()
Types:
Port = port() | atom()
Data = iodata()
Option = force | nosuspend
OptionList = [Option]
Sends data to a port. port_command(Port, Data, [])
equals port_command(Port, Data).
If the port command is aborted false is returned; oth-
erwise, true is returned.
If the port is busy, the calling process will be sus-
pended until the port is not busy anymore.
Currently the following Options are valid:
force:
The calling process will not be suspended if the
port is busy; instead, the port command is forced
through. The call will fail with a notsup exception
if the driver of the port does not support this.
For more information see the ERL_DRV_FLAG_SOFT_BUSY
driver flag.
nosuspend:
The calling process will not be suspended if the
port is busy; instead, the port command is aborted
and false is returned.
我们知道OptionList可以是force和nosuspend,当设置该选项时,如果port处于busy状态,该调用进程不会被挂起,而是会强制执行,如果这个端口的驱动不支持该行为的话,调用程序将收到一个notsup的异常;当nosuspend选项被设置时,如果port处于busy状态,调用进程不会被挂起,而是收到false的返回。
而在erlang:system_monitor/2中,
引用
erlang:system_monitor(MonitorPid, Options) -> MonSettings
Types:
MonitorPid = pid()
Options = [system_monitor_option()]
MonSettings = undefined | {OldMonitorPid, OldOp-
tions}
OldMonitorPid = pid()
OldOptions = [system_monitor_option()]
system_monitor_option() = busy_port
| busy_dist_port
| {long_gc, integer() >= 0}
| {long_schedule, integer() >= 0}
| {large_heap, integer() >= 0}
.....
busy_port:
If a process in the system gets suspended because
it sends to a busy port, a message {monitor, Sus-
Pid, busy_port, Port} is sent to MonitorPid. SusPid
is the pid that got suspended when sending to Port.
如果设置了busy_port选项时,当被monitor的进程因为调用的port处于busy状态而被挂起时,monitor进程会收到{monitor, SusPid, busy_port, Port}消息,SusPid是被监控的进程ID