[erlang]proc_lib源码浅析

源码位置位于安装目录的lib/stdlib/src下。

之前在使用gen_server时,由于之前自己实现过一个gen_server,因此对它内部的机制也能知道个七七八八,最近在用erlang的fsm模块,突然想读一读它得源码,这才突然发现erlang的源码内部还是做了很复杂的工作,尤其是有个“阴魂不散”的模块proc_lib。

无论是gen_server也好,gen_fsm也好,实际上在start的时候,都是调用的底层gen模块的start/start_link函数,由start函数调用do_spawn函数,在这里,就和我之前想象的不一样了,在我之前的印象里,这里应该直接用spawn,回调用户编写的init函数,但是,实际上不是这样的,我们看下源码:

Time = timeout(Options),   
%这个函数获取timeout的时间,默认为infinity

然后开始调用proc_lib的start函数:

proc_lib:start(?MODULE, init_it, [GenMod, self(),
self(), Name, Mod, Args, Options], Time, spawn_opts(Options))

其中,spawn_opts实在获取spawn的参数。

我们跟进到proc_lib的内部,看看start函数做了些什么。

在start函数的第一行返回了一个Pid,那么我们已经能够猜到spawn_opt内部应该是spawn新的进程了。在spawn_opt函数中,首先获取了Parent(proc_lib进程的名字)以及Ancestors,然后调用erlang模块的spawn_opt函数。

这个函数会调用当前进程的init_p方法,现在要注意,此时spawn_opt派生了新的进程,那么原来的parent进程会直接返回一个Pid,这个Pid就被我们之前看到的proc_lib:start的地方接收了,并且继续向下执行了sync_wait(Pid,
Timeout)函数,在sync_wait函数的内部在等待接收消息,如果超过timout的时间没有接收到,就认为失败了,那么我们可以推测,派生出来的进程一定是去执行用户的init的函数去了,并且在执行完后会发给parent进程一个消息。

我们看看我们的猜测对不对,继续跟进到init_p函数内部,我们可以看到函数内部开始搜刮者用户传递进来的Fun的所有信息,并放入进程字典中(真的不喜欢进程字典),然后开始执行用户的Fun函数,然后该进程结束。

是的,你没有看错,你没有发现任何地方向parent进程发送了ack信号,这不科学,因为这意味着parent会因为超时而报错,如果你这么想,你一定是把执行的Fun函数当成了你的init,我们网上看就能发现实际上是gen模块的init_it函数,这里还有个不太好的地方就是init_it调用了函数init_it2…这命名规则着实让人蛋疼……

init_it2回调了用户传入的init_it函数,这个函数是由对应的行为模式编写的init_it函数,比如举个简单的例子,在gen_server的源码中,init_it就调用了用户传入的init函数,并且在执行后,调用init_ack,init_ack函数向parent进程发送了一个成功的消息,之后,gen_server进入loop函数开始接收消息,也就是说,用户的gen_server:start()函数执行完毕。也印证了我们之前的猜想是正确的。

其实,这里我有个疑问,不知道作者为什么不把proc_lib:init_ack函数放在proc_lib的init_p函数的最后执行呢?

我们只是简单的分析了proc_lib的spawn与直接spawn有哪些不同,那么对于我们来说,proc_lib还有哪些直接挖掘的呢?为什么要用proc_lib去包装spawn呢?

翻翻proc_lib的代码,在上文中,我们也可以看出,在spawn之前,保存了不少信息,比如parent进程的信息和相关的函数信息,这些在用procss_info查看时,都可以直观的看到。同时,模块向外暴露的initial_call函数,translate_initial_call函数以及raw_initial_call函数也可以查看。至于函数内部的实现就不多讨论了,比较简单。

最后,我们会发现还有个有趣的函数crash_report,我们分析源码能够看出,对于proc_lib派生的进程来说,退出信号是normal以及shutdown都会被认为是正常退出。其他的出错信息会被error_logger模块捕捉。

最后的最后,还有一个非常有意思的函数,就是hibernate函数,这个函数看起来就让java程序员很亲切,官方手册上,对erlang模块的hibernate函数的解释大约是把当前正在运行的线程处于一个wait的状态,此时,进程会抛弃掉自己的调用栈,并且进行gc,然后wait,直到信箱中接受到了新的消息。很明显的,这种情况应该是当前进程在一段时间内预计不会收到消息,为了节省内存而触发的,在高并发的场景下对内存的节省会起到一定的作用。同时,文档上还说,当进程收到新的消息被唤醒,并且将函数的控制权交给作为参数被传入的Fun。

问题出现了,既然调用栈被抛弃了,那么Fun执行完后何去何从?显然进程这不就直接结束了?当然不是,proc_lib对hibernate做了个封装,当被唤醒时会回调用户传入的函数,比如gen_server,那么wake_hib最终会被回调,我们可以看到,在gen_server中,wake_hib在处理完刚刚接受到得消息后,重新回到了handle_msg的函数中继续等待接收消息。

对于proc_lib,值得说说的也就这么多,代码不是很长,大约700多行,这个模块理解了,本身gen模块东西也不多,那么所有的otp模式也就比较简单了。

恩,就是这样。

时间: 2024-11-18 13:29:17

[erlang]proc_lib源码浅析的相关文章

Volley框架源码浅析(一)

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Voll

PM2源码浅析

PM2工作原理 最近在玩一个游戏,<地平线:黎明时分>,最终Boss是一名叫黑底斯的人,所谓为人,也许不对,黑底斯是一段强大的毁灭进程,破坏了盖娅主进程,从而引发的整个大陆机械兽劣化故事. 为什么要讲这么一段呢,是希望大家可以更好地理解pm2的原理,要理解pm2就要理解god和santan的关系,god和santan的关系就相当于盖娅和黑底斯在pm2中的01世界中,每一行代码每一个字节都安静的工作god就是Daemon进程 守护进程,重启进程,守护node程序世界的安宁,santan就是进程的

Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置

Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这么多,完结 的也没几个,主要还是自己覆盖的太广了,却又不精通,嘿嘿,工作需要,所以写下了本篇博客 一.VMware 12 我选择的虚拟机试VMware,挺好用的感觉,下载VMware就不说了,善用搜索键嘛,这里我提供一个我现在在用的 下载地址:链接:http://pan.baidu.com/s/1k

ReactiveCocoa2 源码浅析

ReactiveCocoa2 源码浅析 标签(空格分隔): ReactiveCocoa iOS Objective-C ? 开车不需要知道离合器是怎么工作的,但如果知道离合器原理,那么车子可以开得更平稳. ReactiveCocoa 是一个重型的 FRP 框架,内容十分丰富,它使用了大量内建的 block,这使得其有强大的功能的同时,内部源码也比较复杂.本文研究的版本是2.4.4,小版本间的差别不是太大,无需担心此问题. 这里只探究其核心 RACSignal 源码及其相关部分.本文不会详细解释里

【Spark Core】任务执行机制和Task源码浅析2

引言 上一小节<任务执行机制和Task源码浅析1>介绍了Executor的注册过程. 这一小节,我将从Executor端,就接收LaunchTask消息之后Executor的执行任务过程进行介绍. 1. Executor的launchTasks函数 DriverActor提交任务,发送LaunchTask指令给CoarseGrainedExecutorBackend,接收到指令之后,让它内部的executor来发起任务,即调用空闲的executor的launchTask函数. 下面是Coars

Volley框架源码浅析(二)

尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列 /** The cache triage queue. */ private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<

Android手势源码浅析-----手势绘制(GestureOverlayView)

Android手势源码浅析-----手势绘制(GestureOverlayView)

【Spark】Stage生成和Stage源码浅析

引入 上一篇文章<DAGScheduler源码浅析>中,介绍了handleJobSubmitted函数,它作为生成finalStage的重要函数存在,这一篇文章中,我将就DAGScheduler生成Stage过程继续学习,同时介绍Stage的相关源码. Stage生成 Stage的调度是由DAGScheduler完成的.由RDD的有向无环图DAG切分出了Stage的有向无环图DAG.Stage的DAG通过最后执行的Stage为根进行广度优先遍历,遍历到最开始执行的Stage执行,如果提交的St

转:Spring FactoryBean源码浅析

http://blog.csdn.net/java2000_wl/article/details/7410714 在Spring BeanFactory容器中管理两种bean 1.标准Java Bean 2,另一种是工厂Bean,   即实现了FactoryBean接口的bean  它不是一个简单的Bean 而是一个生产或修饰对象生成的工厂Bean 在向Spring容器获得bean时  对于标准的java Bean  返回的是类自身的实例 而FactoryBean 其返回的对象不一定是自身类的一