Erlang支持Monitor和Link两种监控进程的方式,使得所有进程可以连成一个整体。当某个进程出错退出时,监控进程会收到该进程退出的消息通知。有了这些特点,使用Erlang建立一个简单,并且健壮的系统就不是什么难事。前面有
文章分析了两种方式的用法,这里分析下monitor和link的实现。
源码分析
monitor 和link实现有点类似,下面以monitor为例做说明(erlang版本R16B02)
erlang:monitor/2的实现
// bif.c 实现 erlang:monitor/2
BIF_RETTYPE monitor_2(BIF_ALIST_2)
{
Eterm target = BIF_ARG_2;
BIF_RETTYPE ret;
DistEntry *dep = NULL;
int deref_de = 0;
/* 目前只支持 erlang:monitor(process, Target) */
if (BIF_ARG_1 != am_process) {
goto error;
}
if (is_internal_pid(target)) { // 如果是本节点进程
local_pid:
ret = local_pid_monitor(BIF_P, target); // 处理本节点进程
} else if (is_external_pid(target)) { // 如果是其他节点进程
dep = external_pid_dist_entry(target);
if (dep == erts_this_dist_entry) // 如果进程归属于本节点,跳到本节点进程处理
goto local_pid;
ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0); // 处理其他节点进程
} else if (is_atom(target)) { // Target是atom处理
ret = local_name_monitor(BIF_P, target);
} else if (is_tuple(target)) { // Target是tuple处理
Eterm *tp = tuple_val(target);
Eterm remote_node;
Eterm name;
if (arityval(*tp) != 2)
goto error;
remote_node = tp[2];
name = tp[1];
if (!is_atom(remote_node) || !is_atom(name)) {
goto error;
}
if (!erts_is_alive && remote_node != am_Noname) {
goto error; /* Remote monitor from (this) undistributed node */
}
dep = erts_sysname_to_connected_dist_entry(remote_node);
if (dep == erts_this_dist_entry) {
deref_de = 1;
ret = local_name_monitor(BIF_P, name);
} else {
if (dep)
deref_de = 1;
ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1);
}
} else {
error:
ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
}
if (deref_de) {
deref_de = 0;
erts_deref_dist_entry(dep);
}
return ret;
}
现在,看下本节点进程的监控处理:
// bif.c 实现本地节点进程监控处理
static BIF_RETTYPE local_pid_monitor(Process *p, Eterm target)
{
BIF_RETTYPE ret;
Eterm mon_ref;
Process *rp;
ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK;
mon_ref = erts_make_ref(p);
ERTS_BIF_PREP_RET(ret, mon_ref);
if (target == p->common.id) { // 如果进程监控自己
return ret;
}
erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK); // 锁住进程link操作,避免进程监控数据被脏写
rp = erts_pid2proc_opt(p, p_locks,
target, ERTS_PROC_LOCK_LINK, // 同样是link锁
ERTS_P2P_FLG_ALLOW_OTHER_X);
if (!rp) {
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
p_locks &= ~ERTS_PROC_LOCK_LINK;
erts_queue_monitor_message(p, &p_locks,
mon_ref, am_process, target, am_noproc);
}
else {
ASSERT(rp != p);
// 当前进程添加监控数据
erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL);
// 目标进程添加被监控数据
erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL);
erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
}
erts_smp_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN);
return ret;
}
实际上,这里只是修改进程的监控数据,监控者和被监控者两份数据。
来看下erts_add_m