[Erlang 0101] Gproc:扩展进程注册机制

简介:
Erlang 进程注册机制目前的限制是:
  • names只能是atom
  • 一个进程只能注册一个name
  • 不能进行高效的搜索和遍历,进程信息的检索是通过遍历检查进程的元数据完成的.
 Ulf T. Wiger的开源项目  Gproc 就是解决上面问题的,难得的是这个项目的文档,范例,测试代码相当完整,还有 专门的论文讲述整个项目的来龙去脉,设计取舍.
 
Gproc是Erlang进程注册机制的加强版,提供了如下原生进程注册没有的功能:
  • 使用任意Erlang Term作为进程的别名
  • 一个进程注册多个别名
  • 支持QLC和match specification高效查询
  • 自动移交已经注册的names和属性到另外的进程
  • .....
那它是怎么做的呢?这些额外的信息是维护在哪里,是ETS吗?动手操练一下,验证想法:
 
复制代码
Eshell V5.9  (abort with ^G)
1> application:start(gproc).
ok
2> ets:i().
id              name              type  size   mem      owner
----------------------------------------------------------------------------
13              code              set   265    10683    code_server
4110            code_names        set   53     7018     code_server
8207            shell_records     ordered_set 0      73       <0.26.0>
ac_tab          ac_tab            set   9      929      application_controller
file_io_servers file_io_servers   set   0      283      file_server_2
global_locks    global_locks      set   0      283      global_name_server
global_names    global_names      set   0      283      global_name_server
global_names_ext global_names_ext  set   0      283      global_name_server
global_pid_ids  global_pid_ids    bag   0      283      global_name_server
global_pid_names global_pid_names  bag   0      283      global_name_server
gproc           gproc             ordered_set 0      73       gproc_sup
gproc_monitor   gproc_monitor     ordered_set 0      73       gproc_monitor
inet_cache      inet_cache        bag   0      283      inet_db
inet_db         inet_db           set   29     554      inet_db
inet_hosts_byaddr inet_hosts_byaddr bag   0      283      inet_db
inet_hosts_byname inet_hosts_byname bag   0      283      inet_db
inet_hosts_file_byaddr inet_hosts_file_byaddr bag   0      283      inet_db
inet_hosts_file_byname inet_hosts_file_byname bag   0      283      inet_db
ok
复制代码

 

 
   应用程序启动之后我们查看ETS表的信息,发现多出来两个表:gproc和gproc_monitor;下面我们就使用Shell进程完成测试,为当前的Shell进程创建别名"Shell",并向它发送消息dvd,之后再shell中flush()查看已经接收到的消息.

复制代码
5> gproc:reg({p,l,"Shell"}).
true
6> gproc:send({p,l,"Shell"},dvd).
dvd
7> flush().
Shell got dvd
ok
复制代码

 

 现在查看一下ETS表的内容,发现里面已经记录了当前Shell进程的注册信息;
 
9> ets:i(gproc).
<1   > {{<0.43.0>,l}}
<2   > {{<0.43.0>,{p,l,"Shell"}},[]}
<3   > {{{p,l,"Shell"},<0.43.0>},<0.43.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok

 

紧接上面我们为Shell再创建一个别名,然后查看ETS
 
复制代码
10> gproc:reg({p,l,"Shell_alia"}).
true
11> ets:i(gproc).
<1   > {{<0.43.0>,l}}
<2   > {{<0.43.0>,{p,l,"Shell"}},[]}
<3   > {{<0.43.0>,{p,l,"Shell_alia"}},[]}
<4   > {{{p,l,"Shell"},<0.43.0>},<0.43.0>,undefined}
<5   > {{{p,l,"Shell_alia"},<0.43.0>},<0.43.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok
 
复制代码

 

下面简单演示一下QLC:
 
复制代码
40>  Q5 = qlc:q([P || {{p,l,'_'},P,C} <- gproc:table( )]).
{qlc_handle,{qlc_lc,#Fun<erl_eval.20.111823515>,
                    {qlc_opt,false,false,-1,any,[],any,524288,allowed}}}
41>
41> qlc:eval(Q5).
[<0.65.0>,<0.61.0>]
42> qlc:e(qlc:q([N || N <- gproc:table()])).
[{{p,l,"Hello"},<0.65.0>,undefined},
{{p,l,"NEW_Process"},<0.61.0>,undefined}]
复制代码

 

上面的几段代码基本上包含了它最重要的feature,我们看下实现,注册name的过程实际上是把注册信息写到了ETS;而发送消息的第一步就是从ETS表中查询name对应的Pid,然后进行发送.以发送为例看一下代码实现:
 
复制代码
https://github.com/esl/gproc/blob/master/src/gproc.erl
 
%% If Key belongs to a unique object (name or aggregated counter), this
%% function will send a message to the corresponding process, or fail if there
%% is no such process. If Key is for a non-unique object type (counter or
%% property), Msg will be send to all processes that have such an object.
%% @end
%%
send(Key, Msg) ->
    ?CATCH_GPROC_ERROR(send1(Key, Msg), [Key, Msg]).

send1({T,C,_} = Key, Msg) when C==l; C==g ->
    if T == n orelse T == a ->
            case ets:lookup(?TAB, {Key, T}) of
                [{_, Pid, _}] ->
                    Pid ! Msg;
                _ ->
                    ?THROW_GPROC_ERROR(badarg)
            end;
       T==p orelse T==c ->
            %% BUG - if the key part contains select wildcards, we may end up
            %% sending multiple messages to the same pid
            lists:foreach(fun(Pid) ->
                                  Pid ! Msg
                          end, lookup_pids(Key)),
            Msg;
       true ->
            erlang:error(badarg)
    end;
send1(_, _) ->
    ?THROW_GPROC_ERROR(badarg).
 
复制代码

 

 
细致的实现
 
 下面的测试我们通过gproc:info查看进程信息,下面的结果注意一下current function属性值,是不是有疑问?
 
 
复制代码
17> P2=spawn(fun() -> gproc:reg({p,l,"NEW_Process"}),receive a -> a end end ).
<0.61.0>
18> ets:i(gproc).
<1   > {{<0.61.0>,l}}
<2   > {{<0.61.0>,{p,l,"NEW_Process"}},[]}
<3   > {{{p,l,"NEW_Process"},<0.61.0>},<0.61.0>,undefined}
EOT  (q)uit (p)Digits (k)ill /Regexp -->q
ok
19> gproc:info(P2).
[{gproc,[{{p,l,"NEW_Process"},undefined}]},
{current_function,{erl_eval,receive_clauses,6}},
{initial_call,{erlang,apply,2}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.25.0>},
{total_heap_size,610},
{heap_size,233},
{stack_size,8},
{reductions,100},
{garbage_collection,[{min_bin_vheap_size,46368},
                      {min_heap_size,233},
                      {fullsweep_after,65535},
                      {minor_gcs,1}]},
{suspending,[]}]
复制代码

 

 
这里内部实现还是做得非常细致的,不是简单的把信息附加在Process_info,比如对current function信息做了修正.
 
复制代码
https://github.com/esl/gproc/blob/master/src/gproc.erl
 
%% We don't want to return the internal gproc:info() function as current
%% function, so we grab the 'backtrace' and extract the call stack from it,
%% filtering out the functions gproc:info/_ and gproc:'-info/1-lc...' entries.
%%
%% This is really an indication that wrapping the process_info() BIF was a
%% bad idea to begin with... :P
%%
info_cur_f(T, Default) ->
    {match, Matches} = re:run(T,<<"\\(([^\\)]+):(.+)/([0-9]+)">>,
                     [global,{capture,[1,2,3],list}]),
    case lists:dropwhile(fun(["gproc","info",_]) -> true;
                   (["gproc","'-info/1-lc" ++ _, _]) -> true;
                   (_) -> false
               end, Matches) of
     [] ->
         Default;
     [[M,F,A]|_] ->
         {current_function,
          {to_atom(M), to_atom(F), list_to_integer(A)}}
    end.
复制代码

 

 
好了今天就到这里.
 
 
最后小图一张:
 
目录
相关文章
|
8天前
|
消息中间件 存储 网络协议
操作系统的心脏:深入理解进程间通信(IPC)机制
在现代计算机系统中,操作系统扮演着至关重要的角色,而进程间通信(IPC)作为操作系统的核心功能之一,极大地影响着系统的性能和稳定性。本文将通过浅显易懂的语言,详细探讨进程间通信的基本原理、主要类型及其实际应用,旨在为读者提供一个清晰且全面的理解和认识。 ##
|
19天前
|
人工智能 Kubernetes 算法
探究操作系统的心脏——进程管理机制
本文深入探讨了操作系统核心组件之一——进程管理机制。进程管理作为操作系统的基础功能,负责协调和控制计算机系统内运行的所有进程,确保系统资源的有效分配与利用。通过详细介绍进程的定义、状态转换、调度算法以及多线程技术等关键概念,本文揭示了进程管理如何支撑起整个操作系统的运行框架,并保障用户任务的顺利执行。同时,文章还讨论了现代操作系统在进程管理方面的创新与挑战,为读者提供了一个全面而深入的理解视角。
31 1
|
26天前
|
算法 调度 UED
探索操作系统的心脏——进程管理机制
本文将深入探讨操作系统中至关重要的部分——进程管理机制。我们将从基本概念入手,逐步解析进程的定义、状态及其在操作系统中的角色。随后,我们会详细讨论进程调度算法,包括先来先服务、短作业优先、时间片轮转和优先级调度等,分析它们的优势与应用情景。最后,通过实例展示这些算法在实际系统运作中的运用,帮助读者更好地理解进程管理的核心原理。
|
1月前
|
消息中间件 安全 Kafka
Python IPC机制全攻略:让进程间通信变得像呼吸一样自然
【9月更文挑战第12天】在编程领域,进程间通信(IPC)是连接独立执行单元的关键技术。Python凭借简洁的语法和丰富的库支持,提供了多种IPC方案。本文将对比探讨Python的IPC机制,包括管道与消息队列、套接字与共享内存。管道适用于简单场景,而消息队列更灵活,适合高并发环境。套接字广泛用于网络通信,共享内存则在本地高效传输数据。通过示例代码展示`multiprocessing.Queue`的使用,帮助读者理解IPC的实际应用。希望本文能让你更熟练地选择和运用IPC机制。
41 10
|
26天前
|
消息中间件 Python
深入理解操作系统的进程间通信(IPC)机制
本文将探讨操作系统中的核心概念——进程间通信(IPC),揭示其在系统运作中的重要性及实现方式。通过分析不同类型的IPC手段,如管道、信号、共享内存等,帮助读者更好地理解操作系统的内部工作原理及其在实际应用中的表现。
36 1
|
2月前
|
消息中间件 算法 Java
深入浅出操作系统:进程管理的艺术掌握Java中的异常处理机制
【8月更文挑战第30天】在数字世界的舞台上,操作系统扮演着导演的角色,精心安排着每一个进程的表演。本文将揭开进程管理的神秘面纱,从进程的诞生到终结,探究它们如何在操作系统的指挥下和谐共舞。通过生动的比喻和直观的代码示例,我们将一同走进操作系统的核心,理解进程调度、同步与通信的内在机制,以及它们对计算生态的重要性。让我们跟随代码的节奏,一起感受操作系统的魅力吧!
|
27天前
|
消息中间件 存储 大数据
深入理解操作系统中的进程间通信(IPC)机制
本文旨在探讨操作系统中进程间通信(IPC)的核心机制与其重要性。通过对不同IPC手段如管道、信号、消息队列及共享内存等的详细解析,揭示它们如何高效地促进进程间的信息交换与同步。文章不仅阐述各种IPC技术的实现原理,还探讨了它们在实际系统应用中的场景与优化策略,为系统开发者提供全面而深入的理解。
|
27天前
|
消息中间件 程序员 数据处理
探究操作系统中的进程间通信(IPC)机制及其在现代软件开发中的应用
本文深入探讨了操作系统中的核心概念——进程间通信(IPC),揭示了其在现代软件开发中的关键作用。通过对各种IPC机制如管道、消息队列、共享内存等的详细分析,本文旨在为读者提供一个清晰的理解框架,帮助他们掌握如何在实际应用中有效利用这些技术以实现进程间的协同工作。此外,文章还将探讨IPC在高并发环境下的性能优化策略,以及如何避免常见的IPC编程错误。通过结合理论与实践,本文不仅适合希望深入了解操作系统原理的技术人员阅读,也对那些致力于提升软件质量和开发效率的程序员具有重要参考价值。
25 0
|
1月前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
30 0
|
1月前
|
消息中间件 安全 数据库
动手实操!Python IPC机制,打造高效协同的进程军团
【9月更文挑战第10天】在软件开发领域,进程间的高效协作对应用性能与稳定性至关重要。Python提供了多种进程间通信(IPC)机制,如管道、消息队列、套接字、共享内存等,帮助开发者构建高效协同的系统。本文将通过动手实践,使用`multiprocessing`模块演示如何利用队列实现进程间通信。示例代码展示了如何创建一个工作进程从队列接收并处理数据,从而实现安全高效的进程交互。通过实际操作,读者可以深入了解Python IPC的强大功能,提升系统的并发处理能力。
45 0