erlang进程监控的实现原理

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值