一、概述

与大多数的进程相反,Erlang中的并发很廉价,派生出一个进程就跟面向对象的语言中分配一个对象的开销差不多。 在启动一个复杂的运算时,启动运算、派生进程以及返回结果后,所有进程神奇的烟消云散,它们的内存、邮箱、所持有的数据库句柄、它们打开的套接字,以及一些不乐意手工清理的东西,都一并消失。

Erlang进程不是操作系统进程,它们由erlang运行时系统实现,比线程要轻量的多,单个erlang系统可以轻易地派生出成百上千个进程。运行时系统中所有的进程都是隔离的,单个进程的内存不与其他进程共享,也不会被濒死或跑疯的进程破坏。

在操作系统中,典型的线程会在地址空间中为自己预留数兆的栈空间(也就是说32位的机器上并发线程最多也就几千个),栈空间溢出便会导致崩溃。erlang进程在启动时栈空间只有几百个字节,并会按需伸缩。

二、示例

 14> Pid = spawn(fun() -> timer:sleep(60000), primes:primelist(100000) end).    %% 派生一个进程,等待1分钟后做素数运算。 素数功能代码已提前编写好。
<0.55.0>
15> erlang:process_info(Pid).
[{current_function,{timer,sleep,1}},
{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.26.0>},
{total_heap_size,233},
{heap_size,233}, %%刚启动的erlang进程所占的堆的大小仅为233字节,栈的大小10个字节。
{stack_size,10},
{reductions,43}, %%创建erlang进程仅消耗了43个reductions, 可见erlang的轻量。
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
17> erlang:statistics(run_queue). %% 进程处于挂起状态,堆、栈、时间片消耗都不会变
0
22> erlang:statistics(run_queue). %% 进程进入运行队列,准备被调度。
1
23> erlang:process_info(Pid).
[{current_function,{primes,'-primelist/3-lc$^0/1-0-',2}},
{initial_call,{erlang,apply,2}},
{status,runnable},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.26.0>},
{total_heap_size,393300},
{heap_size,75113}, %%进程在做素数计算, 堆栈大小随着需要开始增加。 消耗的时间片也会随着计算量增加而增加。
{stack_size,13773},
{reductions,89874499},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,3085}]},
{suspending,[]}]
27> erlang:statistics(run_queue).
0
29> erlang:process_info(Pid).
[{current_function,{primes,'-primelist/3-lc$^0/1-0-',2}},
{initial_call,{erlang,apply,2}},
{status,runnable},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.26.0>},
{total_heap_size,393300},
{heap_size,75113},
{stack_size,87}, %% 随着计算的结束, 栈空间开始收缩, 这里可以看到堆空间没有变,堆的分配是由erlang GC来控制的, 进程结束时由GC来回收。
{reductions,958602600},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,33117}]},
{suspending,[]}]

三、进程的调度

由于Erlang虚拟机对SMP的支持,每个操作系统线程都可以运行在一个调度器上,每个调度器拥有一自己的运行队列,这样避免了多个调度器同时调度在运行队列中的任务产生的冲突,但是如何保证调度队列任务分配的公平性,Erlang引入了一个高效和公平的概念,迁移逻辑。迁移逻辑利用在系统中收集的统计数据,控制和平衡了队列。

Erlang启动模拟器的时候可以加上+S去指定最大调度器数和可用调度器数。可用调度器数可以在模拟器运行时更改。

 -> erl +S 16:8
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:16:8] [async-threads:10] [hipe] [kernel-poll:false] Eshell V5.10.4 (abort with ^G)
1> erlang:system_info(schedulers_online).
8
2> erlang:system_info(schedulers).
16

下面我们尝试起多个erlang进程去做素数运算,然后看看调度队列情况。

 -> erl +S 8:8     %%启动8个调度器同时可用。
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] Eshell V5.10.4 (abort with ^G)
1> erlang:statistics(run_queue). %%之前没有任务进入调度队列
0
2> [spawn(fun() -> timer:sleep(5000), primes:primelist(10000) end) || _ <- lists:seq(1, 10)]. %% 同时启动10个进程做素数运算
[<0.36.0>,<0.37.0>,<0.38.0>,<0.39.0>,<0.40.0>,<0.41.0>,
<0.42.0>,<0.43.0>,<0.44.0>,<0.45.0>]
5> erlang:statistics(run_queue). %%同时有7个进程被调度运行,3个进程出去准备状态
3
7> erlang:statistics(run_queue). %%两个任务被换入。
1
8> erlang:statistics(run_queue). %%任务执行完毕,没有任务处于等待状态。
0

  那么问题来了,在同样的硬件环境下,是不是调度器越多,进程处理的速度越快呢?列出测试结果:

 -> erl +S 4:4     %%%启动4个可用调度器
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
2> [spawn(fun() -> {P1, _P2} = timer:tc(primes, primelist, [100000]), io:format("timer:~p~n", [P1]) end) || _ <- lists:seq(1, 10)].
timer:185078651
timer:190735178
timer:192956743
timer:193186850
timer:220074562
timer:222929652
timer:234756209
timer:235304593
timer:235474721
timer:236500425 -> erl +S 8:8 %%%启动8个可用调度器
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] Eshell V5.10.4 (abort with ^G)
1>
1>
1>
1> [spawn(fun() -> {P1, _P2} = timer:tc(primes, primelist, [100000]), io:format("timer:~p~n", [P1]) end) || _ <- lists:seq(1, 10)].
[<0.35.0>,<0.36.0>,<0.37.0>,<0.38.0>,<0.39.0>,<0.40.0>,
<0.41.0>,<0.42.0>,<0.43.0>,<0.44.0>]
timer:187405676
timer:187568120
timer:188255698
timer:188577806
timer:190819642
timer:191208176
timer:235470698
timer:236842370
timer:237630863
timer:238206383 -> erl +S 16:11
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:16:11] [async-threads:10] [hipe] [kernel-poll:false] Eshell V5.10.4 (abort with ^G)
1> [spawn(fun() -> {P1, _P2} = timer:tc(primes, primelist, [100000]), io:format("timer:~p~n", [P1]) end) || _ <- lists:seq(1, 10)].
[<0.35.0>,<0.36.0>,<0.37.0>,<0.38.0>,<0.39.0>,<0.40.0>,
<0.41.0>,<0.42.0>,<0.43.0>,<0.44.0>] timer:243000833
timer:243636514
timer:244753411
timer:245005027
timer:245296405
timer:245356679
timer:245659526
timer:245662159
timer:245731926
timer:245779971

  从测试结果看,并不是调度器越多越好,也不是越少越好,合适实际应用场景才是最好的。

erlang进程概述的更多相关文章

  1. 从Erlang进程看协程思想

    从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...

  2. erlang进程与操作系统线程

    erlang多进程与多线程: 在erlang开发中,我们面对的最小执行单位是进程,当然这个进程并不是系统层面上的进程,也不是线程.而是基于erlang运行时系统的一个进程.那么erlang的多进程是如 ...

  3. erlang进程监控:link和monitor

    Erlang最开始是为了电信产品而发展起来的语言,因为这样的目的,决定了她对错误处理的严格要求.Erlang除了提供exception,try catch等语法,还支持Link和Monitor两种监控 ...

  4. Erlang进程堆垃圾回收机制

    原文:Erlang进程堆垃圾回收机制 作者:http://blog.csdn.net/mycwq 每一个Erlang进程创建之后都会有自己的PCB,栈,私有堆.erlang不知道他创建的进程会用到哪种 ...

  5. [erl] erlang 进程注册和注销

    想要注册一个进程,必须先要创建一个进程. 如何创建一个进程,可以使用spawn.spawn_link,它们虽然都能创建进程,但是也有微妙的区别: 1)当前进程中创建一个并行进程,当被生成的进程崩溃时, ...

  6. Erlang进程的Link机制

    这篇文章还不是最终版,有时间时,我会再来补充完善. 什么是link Erlang程序基于进程建模,进程之间的交互机制有收发消息,link和monitor.其中,收发消息通常用于正常的进程间通讯,而li ...

  7. Erlang进程间消息接收超时设定

        Erlang消息接收函数,一般都会设计成尾递归调用自己的模式.但是这样的模式,如果没有消息则会无限的等待下去,所以为了不无限等待,这里可以加个超时设定,例如: flush() -> re ...

  8. Erlang 进程被抢占的条件——一个进程长时霸占调度器的极端示例

    最近研究 binary 的实现和各种操作对应的 beam 虚拟机汇编指令,发现有一些指令序列是不可重入的,比如说有的指令构造一个上下文(也就是某种全局状态),然后下一条指令会对这个上下文做操作(具体的 ...

  9. Linux进程概述

    一.介绍 当linux系统中的一个进程运行起来的时候,总是要访问系统的资源,访问文件或者向其他的进程发送信号.系统是否允许其进行这些操作?系统是根据什么来判断该进程的权限?这些问题是和进程信任状(pr ...

随机推荐

  1. 分析比较KafkaWordCount及DierctKafkaWordCount

    参考spark官方文档,Spark Streaming + Kafka Integration Guide,其中提到Spark Streaming如何从Kafka中接收数据.主要有两种方法,一种是使用 ...

  2. casio计算器计算统计数据

    http://blog.csdn.net/pipisorry/article/details/50257319 使用casio计算器计算输入数据均值.标准差和相关系数的方法,lz使用casio fx8 ...

  3. java 二进制数字符串转换工具类

    java 二进制数字符串转换工具类 将二进制转换成八进制 将二进制转换成十进制 将二进制转换成十六进制 将十进制转换成二进制 package com.iteye.injavawetrust.ad; i ...

  4. 【Unity Shaders】概述及Diffuse Shading介绍

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  5. 程序压力测试、性能测试AB、Webbench、Tsung

             负载生成器是一些生成用于测试的流量的程序.它们可以向你展示服务器在高负载的情况下的性能,以及让你能够找出服务器可能存在的问题.为了得到更加客观和准确的数值,应该从远程访问.局域网访问 ...

  6. 网站论坛同步用户,整合api,实现…

    在网上参考了很多资料后,终于完美实现了网站和discuz!nt论坛的双向整合,整合后网站和论坛之间可以同步注册.登录.退出和修改登录密码操作. 本系统的实现形式是新云CMS网站(ASP)和Discuz ...

  7. 《java入门第一季》之面向对象(如何使用帮助文档)

    1:打开帮助文档 2:点击显示,找到索引,看到输入框 3:知道你要找谁?以Scanner举例 4:在输入框里面输入Scanner,然后回车 5:看包 java.lang包下的类不需要导入包,其他的全部 ...

  8. 升级CentOS5.6_X64 python2.4.3到2.7

    本文转自:http://hxl2009.blog.51cto.com/779549/1031310 升级CentOS 5.6 64位版python到2.7.31. 背景CentOS 5.6自带的Pyt ...

  9. AngularJS进阶(十六)脏值检查

    脏值检查 注:请点击此处进行充电! 需求 在项目开发过程中,需要对药店信息进行更改.如下图所示.现在的需求是:当药店信息没有发生变化时,点击"更新信息"按钮,提示"药店信 ...

  10. zookeeper+kafka集群安装之一

    zookeeper+kafka集群安装之一 准备3台虚拟机, 系统是RHEL64服务版. 1) 每台机器配置如下: $ cat /etc/hosts ... # zookeeper hostnames ...