- 浏览: 101079 次
- 性别:
- 来自: 杭州
最新评论
-
johncan:
但是如果绑定错误,反而会有反效果,请问如何看是否绑定错误?是否 ...
erlang程序优化点的总结(持续更新) -
mahengyang:
如何使用mnesia:select/4分页查询%%对事务封装了 ...
mnesia监控项目 -
zjjxxl:
mysql代码高手,我们这里mysql5.6长时间运行内存过高 ...
innodb对B树游标的定位过程以及对“小于(等于)B树最小记录”的特殊处理 -
wqtn22:
是原创啊,霸爷mryufeng认证的,增加引用计数和减少引用计 ...
erlang NIF部分接口实现(三)持久资源 -
magicxiao:
enif_release_resource当引用计数为0的时候 ...
erlang NIF部分接口实现(三)持久资源
在R15B01上,遇到一个gen_tcp/gen_udp:controlling_process(Port, self())导致的port泄露问题,下列链接详细的说明了产生问题的步骤:
https://github.com/erlang/otp/commit/944a57a11a79c5a9bb2f554c921e2e00e7d56c91
该问题在R15B03得到了修复,此处分析这个问题如下:
1> {ok,Port} = gen_udp:open(9000, [binary]).
{ok,#Port<0.581>}
2> i(0,31,0).
[{current_function,{c,pinfo,1}},
{initial_call,{erlang,apply,2}},
{status,running},
{message_queue_len,0},
{messages,[]},
{links,[<0.25.0>,#Port<0.581>]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.24.0>},
{total_heap_size,5168},
{heap_size,2584},
{stack_size,27},
{reductions,3814},
{garbage_collection,[{min_bin_vheap_size,46368},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,1}]},
{suspending,[]}]
此时,这个新建的port已经关联到shell进程<0,31,0>上。
3> gen_udp:controlling_process(Port, self()).
ok
4> i(0,31,0).
[{current_function,{c,pinfo,1}},
{initial_call,{erlang,apply,2}},
{status,running},
{message_queue_len,0},
{messages,[]},
{links,[<0.25.0>]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.24.0>},
{total_heap_size,5168},
{heap_size,2584},
{stack_size,27},
{reductions,8889},
{garbage_collection,[{min_bin_vheap_size,46368},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,7}]},
{suspending,[]}]
controlling_process到自身后,到port的link消失了。
分析这个过程的代码,发现如下问题:
gen_udp.erl
controlling_process(S, NewOwner) ->
inet:udp_controlling_process(S, NewOwner).
inet.erl
udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
case erlang:port_info(S, connected) of
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_ ->
{ok, A0} = prim_inet:getopt(S, active),
prim_inet:setopt(S, active, false),
udp_sync_input(S, NewOwner),
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S),
prim_inet:setopt(S, active, A0),
ok
catch
error:Reason ->
{error, Reason}
end
end.
一次controlling_process包括两个动作:首先在新进程上port_connect这个port,接着在原进程上unlink这个port。port_connect与unlink均为bif函数。
BIF_RETTYPE port_connect_2(BIF_ALIST_2)
{
Port* prt;
Process* rp;
Eterm pid = BIF_ARG_2;
if (is_not_internal_pid(pid)) {
error:
BIF_ERROR(BIF_P, BADARG);
}
prt = id_or_name2port(BIF_P, BIF_ARG_1);
if (!prt) {
goto error;
}
rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
pid, ERTS_PROC_LOCK_LINK);
if (!rp) {
erts_smp_port_unlock(prt);
ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
goto error;
}
erts_add_link(&(rp->nlinks), LINK_PID, prt->id);
erts_add_link(&(prt->nlinks), LINK_PID, pid);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
prt->connected = pid; /* internal pid */
erts_smp_port_unlock(prt);
#ifdef USE_VM_PROBES
if (DTRACE_ENABLED(port_connect)) {
DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
dtrace_pid_str(prt->connected, process_str);
erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
dtrace_proc_str(rp, newprocess_str);
DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
}
#endif
BIF_RET(am_true);
}
port_connect将在port与新进程上均添加一个link进行关联,但是存在一个问题。
int erts_add_link(ErtsLink **root, Uint type, Eterm pid)
{
void *tstack[STACK_NEED];
int tpos = 0;
int dstack[STACK_NEED+1];
int dpos = 1;
int state = 0;
ErtsLink **this = root;
Sint c;
dstack[0] = DIR_END;
for (;;) {
if (!*this) { /* Found our place */
state = 1;
*this = create_link(type,pid);
break;
} else if ((c = CMP(pid,(*this)->pid)) < 0) {
/* go left */
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
this = &((*this)->left);
} else if (c > 0) { /* go right */
dstack[dpos++] = DIR_RIGHT;
tstack[tpos++] = this;
this = &((*this)->right);
} else { /* Equal key is an error for monitors */
return -1;
}
}
insertion_rotation(dstack, dpos, tstack, tpos, state);
return 0;
}
在erts_add_link中,会遍历进程/port的link表,如果发现link节点已经在自身的link表中,则返回-1,但是在上层调用者port_connect_2处并未检查这个错误,而是直接返回。对于controlling_process到自身的场景中,此处返回后,立即调用unlink。
BIF_RETTYPE unlink_1(BIF_ALIST_1)
{
Process *rp;
DistEntry *dep;
ErtsLink *l = NULL, *rl = NULL;
/*
* SMP specific note concerning incoming exit signals:
* We have to have at least the status lock during removal of
* the link half on current process, and check for and handle
* a present pending exit while the status lock is held. This
* in order to ensure that we wont be exited by a link after
* it has been removed.
*
* (We also have to have the link lock, of course, in order to
* be allowed to remove the link...)
*/
if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) {
trace_proc(BIF_P, BIF_P, am_unlink, BIF_ARG_1);
}
if (is_internal_port(BIF_ARG_1)) {
Port *pt = erts_id2port_sflgs(BIF_ARG_1,
BIF_P,
ERTS_PROC_LOCK_MAIN,
ERTS_PORT_SFLGS_DEAD);
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
#ifdef ERTS_SMP
if (ERTS_PROC_PENDING_EXIT(BIF_P)) {
if (pt)
erts_smp_port_unlock(pt);
goto handle_pending_exit;
}
#endif
l = erts_remove_link(&BIF_P->nlinks, BIF_ARG_1);
ASSERT(pt || !l);
if (pt) {
rl = erts_remove_link(&pt->nlinks, BIF_P->id);
erts_smp_port_unlock(pt);
if (rl)
erts_destroy_link(rl);
}
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS);
if (l)
erts_destroy_link(l);
BIF_RET(am_true);
}
else if (is_external_port(BIF_ARG_1)
&& external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) {
BIF_RET(am_true);
}
if (is_not_pid(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
…
}
代码篇幅较大,这里仅列出port相关的部分,其中对erts_remove_link的调用是关键部分,它将移除原先通过erts_add_link加入的link节点。
ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid)
{
ErtsLink **tstack[STACK_NEED];
int tpos = 0;
int dstack[STACK_NEED+1];
int dpos = 1;
int state = 0;
ErtsLink **this = root;
Sint c;
int dir;
ErtsLink *q = NULL;
dstack[0] = DIR_END;
for (;;) {
if (!*this) { /* Failure */
return NULL;
} else if ((c = CMP(pid,(*this)->pid)) < 0) {
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
this = &((*this)->left);
} else if (c > 0) { /* go right */
dstack[dpos++] = DIR_RIGHT;
tstack[tpos++] = this;
this = &((*this)->right);
} else { /* Equal key, found the one to delete */
q = (*this);
if (q->right == NULL) {
(*this) = q->left;
state = 1;
} else if (q->left == NULL) {
(*this) = q->right;
state = 1;
} else {
dstack[dpos++] = DIR_LEFT;
tstack[tpos++] = this;
state = delsub((ErtsMonitorOrLink **) this);
}
break;
}
}
while (state && ( dir = dstack[--dpos] ) != DIR_END) {
this = tstack[--tpos];
if (dir == DIR_LEFT) {
state = balance_left((ErtsMonitorOrLink **) this);
} else {
state = balance_right((ErtsMonitorOrLink **) this);
}
}
return q;
}
可以看到,在这个场景中,由于原先进程与port是link在一起的,此处必然能将它们unlink。
回到udp_controlling_process:
udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
case erlang:port_info(S, connected) of
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_ ->
{ok, A0} = prim_inet:getopt(S, active),
prim_inet:setopt(S, active, false),
udp_sync_input(S, NewOwner),
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S),
prim_inet:setopt(S, active, A0),
ok
catch
error:Reason ->
{error, Reason}
end
end.
综上所述,在controlling_process到自身的场景中,erlang:port_connect不能将port再次加入原进程的link列表,而unlink却会正常地将port与原进程断开,此后,port变为无主port。
此时,若需要关闭这个port,可以通过erlang:port_close关闭。
5> lists:last(erlang:ports()).
#Port<0.581>
6> P = lists:last(erlang:ports()).
#Port<0.581>
7> erlang:port_close(P).
true
8> inet:i().
ok
修复这个bug的代码,作了一项额外检查,若发现新进程与原进程相同,则直接返回ok。
udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
case erlang:port_info(S, connected) of
{connected, NewOwner} ->
ok;
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_ ->
{ok, A0} = prim_inet:getopt(S, active),
prim_inet:setopt(S, active, false),
udp_sync_input(S, NewOwner),
try erlang:port_connect(S, NewOwner) of
true ->
unlink(S),
prim_inet:setopt(S, active, A0),
ok
catch
error:Reason ->
{error, Reason}
end
end.
发表评论
-
erlang虚拟机topology不符导致启动后crash
2013-12-24 15:49 2010线上有一台t4的机器,这些机器的cpu topo是经过伪造 ... -
mnesia监控项目
2013-07-07 13:34 4511mnesia在运行时提供了大量的统计量,对这些统计量进行监 ... -
mnesia的index_read的性能问题
2013-06-23 16:12 3925我们在程序开发过程中,存在如下的一段代码: F = fu ... -
erlang程序优化点的总结(持续更新)
2013-03-03 15:40 11603转载请注明出处 注意,这里只是给出一个总结,具体性能 ... -
gen_tcp的close与delay_send交叉问题
2013-01-07 22:07 6895铁血的同学遇到这样一个问题,与之前4399同学遇到的问题类似, ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(五)套接字发送的应对
2012-06-30 23:35 3411复制前文的应对方法概 ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(四)文件写的应对
2012-06-30 23:08 1979对于“进程允许异步投递,但进程内部有调用port(receiv ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(三)典型场景
2012-06-30 22:28 2034上文解释了问题成因,即进程允许异步投递, 但进程内部有调用p ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(二)成因
2012-06-30 22:06 3016上文介绍了简单的问题场景,现在来分析下产生的原因。 cons ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(一)场景
2012-06-30 20:37 2310在进行项目开发时,有这么一个需求:一个生产者不断产生消息,并将 ...
相关推荐
标题中的"otp_win64_R15B01.rar"是一个RAR格式的压缩文件,它包含的是OTP(Open Telecom Platform)在Windows 64位操作系统上的版本R15B01。OTP是Ericsson公司开发的一个开源软件框架,主要用于构建可靠、可扩展和...
标题中的"otp_win32_R15B01.rar"是一个RAR格式的压缩文件,它包含了一个名为"otp_win32_R15B01"的程序或软件包。OTP,全称是“One-Time Password”,通常指的是在安全领域中使用的一次性密码技术,用于增强账户的...
标题"installer_r15-windows_android_SDK"表明这是一个针对Windows系统的特定版本(r15)的Android SDK安装程序。在这个压缩包中,我们主要关注的是SDK的组成部分及其功能。 1. **SDK Manager**:这是Android SDK的...
OPPO R15 TWRP刷入重启REC后会黑屏一会等15秒按一下电源键即可进TWRP
This file contains a wind turbine model. It includes a three-dimensional mechanical model of the tower, nacelle, and blades modeled in Simscape Multibody, hydraulic pitch actuators, electrical yaw ...
model of wind turbina for matlab 2015a
android-ndk-r15c-linux-x86_64.zip 分包压缩的,合并后为一个压缩包,再解压即可.
总的来说,天正给排水软件是一个强大的工具,它通过其不断进化的不同版本,持续为建筑设计领域的给排水设计提供了强大的支持。随着R15至R17的升级,用户可以享受到更高效、更精确的设计体验。对于建筑设计师而言,...
R15最新版本3GPP TS 36.323 V15.2.0 (2018-12) 3rd Generation Partnership Project; Technical Specification Group Radio Access Network; Evolved Universal Terrestrial Radio Access (E-UTRA); Packet Data ...
android-ndk-r15c-linux-x86_64_002 可在linux下编译。
- **Agent for Microsoft SQL Server**:作为CA ARCserve Backup r15 for Windows的一个组件,此代理程序专门用于备份运行在Microsoft SQL Server上的数据库。 - **Client Agent for Windows**:同样作为CA ...
《深入解析Android SDK r15 for Windows》 在Android应用开发的世界里,...虽然随着技术的发展,更高级别的SDK版本已经出现,但理解每一个版本的特点和功能,对于开发者来说,仍然是提升工作效率和解决问题的关键。
【Android SDK R15详解】 Android SDK(Software Development Kit)是...随着技术的迭代,新的SDK版本不断推出,增加了更多功能和改进,但R15作为历史的一个里程碑,对于理解Android开发的历史和发展具有重要意义。
R15版本是该软件的一个重要迭代,它以其精确、公正的评测标准在IT行业内获得了广泛的认可。CINEBENCH R15是基于Cinema 4D渲染引擎,一个专业级别的3D建模和动画软件,因此它的测试结果能反映出处理器在处理复杂3D...
平台工具包"platform-tools_r15"是Android SDK的一个重要组成部分,主要包含了Android开发者日常工作中不可或缺的工具,尤其是adb(Android Debug Bridge)。这个版本号"r15"指的是该平台工具的第15个修订版,通常每...
android-ndk-r15c-linux-x86_64_003 可在linux下编译。
R15 18年12月份最新版本。 3rd Generation Partnership Project; Technical Specification Group Radio Access Network; Evolved Universal Terrestrial Radio Access (E-UTRA); Physical layer procedures (Release...
697182853457361R15_通用.R15A.apk
android-ndk-r15c-linux-x86_64 004 ,linux 下的 ndk 开发包,r15c
本文档对 5G-R15 版本的特性进行了描述,基于 2017 年 12 月份完成的 5GNR 规范,3GPP 组织已经提交 5G 方案的初始描述文件到 ITU,这是 IMT-2020 提交和评估过程中的重要一步。报告总结了 3GPPR15(2017/12 版本)的...