Erlang supports monitor and link two monitoring processes so that all processes can be connected as a whole. When a process exits with an error, the monitoring process receives a message notification that the process exits. With these features, it is not difficult to build a simple, robust system using Erlang. In the previous article, we analyzed the usage of two methods, and analyzed the implementation of monitor and link.
Source AnalysisMonitor and link implementations are somewhat similar, as shown in monitor example (Erlang version r16b02)the realization of ERLANG:MONITOR/2
BIF.C implements Erlang:monitor/2bif_rettype monitor_2 (bif_alist_2) {eterm target = bif_arg_2; BIF_RETTYPE ret; Distentry *DEP = NULL; int deref_de = 0; /* Currently only supports erlang:monitor (process, Target) */if (bif_arg_1! = am_process) {goto error; } if (Is_internal_pid (target)) {///If this node process Local_pid:ret = Local_pid_monitor (bif_p, target);//processing this node process} else if (Is_external_pid (target)) {///if it is another node process dep = External_pid_dist_entry (target); if (dep = = erts_this_dist_entry)//if the process belong to this node, skip to this node process processing goto Local_pid;ret = Remote_monitor (bif_p, bif_arg_1, Bif_arg_2, DEP, Target, 0); Process other node processes} else if (Is_atom (target)) {//target is atom processing ret = Local_name_monitor (bif_p, target); } else if (Is_tuple (target)) {//target is a tuple processing 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 (n AME) {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;}
Now, look at the monitoring process for this node process:
BIF.C implement local node process monitoring process 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) {//If the process monitors itself return RET; } erts_smp_proc_lock (P, Erts_proc_lock_link); Lock process link operation to avoid process monitoring data being dirty write RP = Erts_pid2proc_opt (p, p_locks, Target, erts_proc_lock_link,//is also LINK lock 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);//The current process adds monitoring data erts_add_monitor (&erts_p_monitors (p), Mon_origin, Mon_ref, Target, NIL);//target process to add monitored data 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;}
In fact, this is just the process of modifying the monitoring data, the monitor and the monitored two copies of data. Take a look at the implementation of Erts_add_monitor:
ERL_MONITORS.C implementation process increased monitoring information void Erts_add_monitor (Ertsmonitor **root, Uint type, eterm ref, Eterm PID, eterm name) {
void *tstack[stack_need]; int tpos = 0; int dstack[stack_need+1]; int dpos = 1; int state = 0; Ertsmonitor **this = root; Sint C; Dstack[0] = dir_end; for (;;) {if (!*this) {/* Found our Place * * state = 1; *this = Create_monitor (type,ref,pid,name); break;} else if ((c = Cmp_mon_ref (REF, (*this)->ref)) < 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 a error for monitors * /erl_exit (1, "insertion of already present monitor!"); break;} } Insertion_rotation (Dstack, Dpos, Tstack, TPOs, state);}
Take a look at this macro, the process structure of the monitoring data. That is, each process has a monitoring data, recording the monitoring and monitoring information, saved as the AVL tree structure.
#define Erts_p_monitors (P) ((p)->common.u.alive.monitors)
processing of process monitoringIn front of the analysis, the monitoring process is only marked in the monitored process, and when the process exits, how is it handled?
ERL_MONITOR.C triggers all monitor (traversing the monitor data, performing doit function callbacks) void Erts_sweep_monitors (Ertsmonitor *root, Void (*doit) ( Ertsmonitor *, void *), void *context) { ertsmonitor *tstack[stack_need]; int tpos = 0; int dstack[stack_need+1]; int dpos = 1; int dir; Dstack[0] = dir_end; for (;;) {if (root = NULL) { if (dir = dstack[dpos-1]) = = Dir_end) {return; } if (dir = = dir_left) {/* still have dir_right to do */dstack[dpos-1] = Dir_right;root = (tstack[tpos-1])->right; } else {/* Stacktop is a object to being deleted */(*doit) (tstack[--tpos],context);//Execution callback--dpos;root = NULL; }} else { dstack[dpos++] = dir_left; tstack[tpos++] = root; root = Root->left;}} }
when will the monitoring callback be triggered? 1. Process shutdown 2. Distributed port shutdown
The above will trigger the monitoring callback, which is explained by the process shutdown:
ERL_PROCESS.C process off processing (truncated) void erts_continue_exit_process (Process *p) { //... Mon = Erts_p_monitors (P); LNK = Erts_p_links (P); //... if (LNK) {//Link processing declaretmpheap (tmp_heap,4,p); Eterm exit_tuple; Uint EXIT_TUPLE_SZ; eterm* HP; Usetmpheap (4,p); hp = &tmp_heap[0];exit_tuple = TUPLE3 (HP, Am_exit, p->common.id, reason); EXIT_TUPLE_SZ = Size_ Object (exit_tuple); { Exitlinkcontext context = {p, reason, Exit_tuple, EXIT_TUPLE_SZ}; Erts_sweep_links (LNK, &doit_exit_link, &context);} Unusetmpheap (4,p); } {//monitor processing Exitmonitorcontext context = {reason, p};erts_sweep_monitors (mon,&doit_exit_monitor,&context) ; /* Allocates tmpheap, but we had none here * /}//...}
Look at the processing of the callback function in the above code
ERL_PROCESS.C process shutdown monitoring handles static void Doit_exit_monitor (Ertsmonitor *mon, void *vpcontext) {Exitmonitorcontext *pcontext = Vpcontext; Distentry *DEP; Ertsmonitor *rmon; Process *RP; if (Mon->type = = Mon_origin) {//If the process has monitored other processes, delete the monitored information for other processes */We are monitoring someone else, we need to demonitor that One.. */if (Is_atom (mon->pid)) {/* Remote by name */ASSERT (Is_node_name_atom (mon->pid)); DEP = Erts_sysname_to_connected_dist_entry (mon->pid); if (DEP) {///if the process monitors the remote node's process erts_smp_de_links_lock (DEP);//deletes distentry monitoring information Rmon = Erts_remove_monitor (& (dep-> Monitors), mon->ref); Erts_smp_de_links_unlock (DEP); if (Rmon) {///Then notifies the remote node to remove the monitored information ertsdsigdata DSD; int code = Erts_dsig_prepare (&DSD, DEP, NULL, Erts_dsp_no_lock, 0); if (code = = erts_dsig_prep_connected) {code = Erts_dsig_send_demonitor (&dsd,rmon->pid,mon->name,mon-> ref,1); ASSERT (Code = = ERTS_DSIG_SEND_OK); } erts_destroy_monitor (Rmon);} Erts_deref_dist_entry (DEP); }} else {ASSERT (Is_pid (mon->pid)); if (Is_internal_pid (mon->pid)) {///If it is the current node process RP = Erts_pid2proc (NULL, 0, Mon->pid, erts_proc_lock_link); if (!RP) { Goto done;} Delete monitoring information for monitored processes Rmon = erts_remove_monitor (&erts_p_monitors (RP), mon->ref); Erts_smp_proc_unlock (RP, Erts_proc _lock_link); if (Rmon = = NULL) {goto done;} Erts_destroy_monitor (Rmon); } else {/* Remote by PID */assert (Is_external_pid (Mon->pid));d EP = External_pid_dist_entry (Mon->pid); ASSERT (dep! = NULL), if (DEP) {erts_smp_de_links_lock (DEP);//First delete distentry monitoring information Rmon = Erts_remove_monitor (dep. &) ->monitors), mon->ref); Erts_smp_de_links_unlock (DEP); if (Rmon) {//Then notifies the remote node to remove the monitored information ertsdsigdata dsd;int code = erts_dsig_prepare (&DSD, DEP, NULL, Erts_dsp_no_lock, 0); I F (Code = = erts_dsig_prep_connected) {code = Erts_dsig_send_demonitor (&DSD, Rmon->pid, Mon->pid, MO N->ref, 1); ASSERT (Code = = ERTS_DSIG_SEND_OK);} Erts_destroy_monitor (Rmon); }}}}} or else {//If there is a process monitoring the process, notify the monitoring process assert (Mon->type = = Mon_target); ASSERT (Is_pid (mon->pid) | | is_internal_port (MON->PID)), if (Is_internal_port (mon->pid)) {//If the monitoring process is the port of this node Port *prt = Erts_id2port (mon->pid); if (prt = = NULL) {goto done; } erts_fire_port_monitor (PRT, mon->ref); Erts_port_release (PRT); } else if (Is_internal_pid (mon->pid)) {//If the monitoring process is eterm watched for this node process; Declaretmpheapnoproc (lhp,3); Ertsproclocks rp_locks = (Erts_proc_lock_link | Erts_proc_locks_msg_send); RP = Erts_pid2proc (NULL, 0, Mon->pid, rp_locks); if (rp = = NULL) {goto done; } usetmpheapnoproc (3);//First remove the monitoring information from the monitoring process Rmon = Erts_remove_monitor (&erts_p_monitors (RP), mon->ref); if (Rmon) {erts_destroy_monitor (rmon); watched = (Is_atom (mon->name)? TUPLE2 (LHP, Mon->name, Erts_this_dist_entry->sysname): pcontext->p->common.id);//Then turn the process off message to notify the monitoring process {' Down ', Ref,process,pid,reason}erts_queue_monitor_message (RP, &Amp;rp_locks, Mon->ref, am_process, watched, Pcontext->reason); } unusetmpheapnoproc (3); /* Else:demonitor While we exited, i.e. does nothing ... */Erts_smp_proc_unlock (RP, rp_locks);} else {//If the monitoring process is a remote node process ASSERT (Is_external_pid (mon->pid)); DEP = External_pid_dist_entry (mon->pid); ASSERT (dep! = NULL); if (DEP) {erts_smp_de_links_lock (DEP);//delete distentry monitoring information Rmon = Erts_remove_monitor (& (dep->monitors), mon- >REF); Erts_smp_de_links_unlock (DEP); if (Rmon) {//Then notifies the remote node that the process exits the message ertsdsigdata DSD; int code = Erts_dsig_prepare (&DSD, DEP, NULL, Erts_dsp_no_lock, 0); if (code = = erts_dsig_prep_connected) {code = erts_dsig_send_m_exit (&DSD, Mon->pid, (rmon->name! = NIL ? Rmon->name:rmon->pid), Mon->ref, Pcontext->reason); ASSERT (Code = = ERTS_DSIG_SEND_OK); } erts_destroy_monitor (Rmon);} }}} Done:/* As the monitors is previously removed from the process, distrIbution operations won't cause monitors to disappear, we can safely delete it. */Erts_destroy_monitor (Mon);}
implementation of cross-node process monitoringIn front of the processing of this node, that cross-node process monitoring is how to achieve, what is the difference?
BIF.C process monitoring for cross-node processes static Bif_rettype Remote_monitor (Process *p, Eterm bifarg1, Eterm bifarg2, Distentry *DEP, Eterm Target, int byname) {Ertsdsigdata DSD; BIF_RETTYPE ret; int code; Erts_smp_proc_lock (P, Erts_proc_lock_link); Code = Erts_dsig_prepare (&DSD, DEP, p, erts_dsp_rlock, 0); Get the status of the distributed port switch (code) {case Erts_dsig_prep_not_alive://port is not activated, use trap processing/* Let the dmonitor_p trap handle I T */Case erts_dsig_prep_not_connected://Port not connected, use trap to handle Erts_smp_proc_unlock (p, Erts_proc_lock_link); ERTS_BIF_PREP_TRAP2 (ret, Dmonitor_p_trap, p, Bifarg1, BIFARG2); Use trap processing to invoke Erlang:dmonitor_p/2break on the next schedule; Case erts_dsig_prep_connected://Port connected, can send data if (! ( Dep->flags & Dflag_dist_monitor) | | (ByName &&!) (Dep->flags & Dflag_dist_monitor_name))) {Erts_smp_de_runlock (DEP); Erts_smp_proc_unlock (P, Erts_proc_lock_link); Erts_bif_prep_error (ret, p, badarg);} else {eterm p_trgt, p_name, D_name, Mon_ref; Mon_ref = ERTs_make_ref (P); if (byname) {p_trgt = Dep->sysname;p_name = Target;d_name = target; } else {p_trgt = Target;p_name = Nil;d_name = NIL; } erts_smp_de_links_lock (DEP);//Current process Add monitoring data erts_add_monitor (&erts_p_monitors (P), Mon_origin, Mon_ref, P_TRGT, P_name)///distentry add monitored Data erts_add_monitor (& (Dep->monitors), Mon_target, Mon_ref, P->common.id, D_ name); Erts_smp_de_links_unlock (DEP); Erts_smp_de_runlock (DEP); Erts_smp_proc_unlock (P, erts_proc_lock_link);//Send monitoring message to remote Node code = erts_dsig_send_monitor (&DSD, P->common.id, Target, Mon_ref); if (code = = Erts_dsig_send_yield) erts_bif_prep_yield_return (ret, p, mon_ref); Elseerts_bif_prep_ret (RET, mon_ref);} Break Default://Other port status, such as Port will be suspended assert (! "Invalid DSig prepare result"); Erts_bif_prep_error (ret, p, exc_internal_error); } return ret;}
Next, look at the message sent to the Remote node processing.
DIST.C monitoring messages to remote node int erts_dsig_send_monitor (Ertsdsigdata *DSDP, Eterm Watcher, Eterm watched, eterm ref) { Eterm ctl; Declaretmpheapnoproc (ctl_heap,5); int res; Usetmpheapnoproc (5); CTL = TUPLE4 (&ctl_heap[0], Make_small (dop_monitor_p), watcher, watched, ref);//Construct message {dop_monitor_p, localpid, Remotepidorname, REF} issued to the remote node res = Dsig_send (DSDP, CTL, The_non_value, 0); Unusetmpheapnoproc (5); return res;}
Look at the processing after receiving this message remotely.
DIST.C handles messages sent by other nodes (truncated) int erts_net_message (Port *prt, Distentry *dep, byte *hbuf, Erldrvsizet Hlen, by Te *buf, Erldrvsizet len) {//... switch (type = Unsigned_val (Tuple[1])) {//...//processing {dop_monitor_p, Remote PID, local PID or name, ref} case dop_monitor_p: {/* A remote process wants to MONITOR us, we get: {dop_monitor_p, R emote PID, local PID or name, ref} */eterm name;if (tuple_arity! = 4) {goto invalid_message;} Watcher = tuple[2];watched = Tuple[3]; /* Local proc to monitor */ref = Tuple[4];if (Is_not_ref (ref)) {goto invalid_message;} if (Is_atom (watched)) {name = watched; RP = erts_whereis_process (NULL, 0, watched, Erts_proc_lock_link, erts_p2p_flg_allow_other_x);} else {name = NIL; RP = erts_pid2proc_opt (NULL, 0, watched, Erts_proc_lock_link, erts_p2p_flg_allow_other_x);} if (!RP) {//if the monitored process does not exist, the reply process exits the message ertsdsigdata DSD; int code; Code = Erts_dsig_prepare (&DSD, DEP, NULL, Erts_dsp_no_locK, 0); if (code = = erts_dsig_prep_connected) {code = Erts_dsig_send_m_exit (&DSD, Watcher, watched, ref, AM_NOPROC); ASSERT (Code = = ERTS_DSIG_SEND_OK); }}else {if (Is_atom (watched)) watched = rp->common.id; Erts_smp_de_links_lock (DEP);//distentry Add monitoring Data Erts_add_monitor (& (Dep->monitors), Mon_origin, ref, watched, name);//process add monitored data erts_add_monitor (&erts_p_monitors (RP), Mon_target, ref, Watcher, name); Erts_smp_de_links_unlock (DEP); Erts_smp_proc_unlock (RP, Erts_proc_lock_link);} Break } //...}
compare the processing of this node and the cross-nodeThis node process monitoring processing is as follows: (process x monitoring process Y)
/********************************************************************** * process X Process Y * +----- --------+ +-------------+ * Type: | Mon_origin | | Mon_target | * +-------------+ +-------------+ * Pid: | Pid (Y) | | Pid (X) | * +-------------+ +-------------+ ****************************************************** ****************/
Cross-node Processing: (Node A's process x monitors node B's process Y)
/******************************************************** * Node A | Node B *---------------------------------+----------------------------------* Process X (@A) distentry @A Distentry @B Process Y (@B) * for node B for node A * +----------- --+ +-------------+ +-------------+ +-------------+ * Type: | Mon_origin | | Mon_target | | Mon_origin | | Mon_target | * +-------------+ +-------------+ +-------------+ +-------------+ * Pid: | Atom (node B) | | Pid (X) | | Pid (Y) | | Pid (X) | * +-------------+ +-------------+ +-------------+ +-------------+ ********************************************* *************************/
contrast is one more step distentry processing, which is determined by the instability of the cross-node network. The remote process has an exception, either the process has been hung or a node connection problem may have occurred. When an exception occurs on a remote node, it triggers the processing of the node's associated process.
Summaryfrom the above analysis, it can be seen that process monitoring is actually just marking the monitored process, and then handling all monitoring processes when the monitored process is out of the ordinary.
Reference: http://blog.csdn.net/mycwq/article/details/46961489
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Analysis of the implementation of Erlang process monitoring