`
jzhihui
  • 浏览: 268136 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Erlang并发机制 - 进程

阅读更多

在了解Erlang的并发机制之前,我们先来看一下ErlangJava的并发性能对比,一个是并发单元的创建时间,一个是并发单元之间的消息通讯时间(纵坐标代表时间,横坐标代表并发数量):

   

(测试程序及说明见这里,原测试时间比较早了,于是在自己的虚拟机上重新跑了下(CenterOS 63G内存);JVM生成线程数量控制,可见这里,)

 

         从上面的测试结果来看,Erlang的并发效率要比Java好很多,不在一个数量级上,ErlangJava1000倍左右(这不能说明在所有场景下Erlang的并发性能都优于Java,比如消息传递方面,消息的大小可能对这个结果也有影响)。那么Erlang的并发机制相比Java到底好在哪里,本文及后续几篇文章的目的就是搞清楚这个问题。

 

         在《Erlang 编程指南》一书中,对Erlang的并发单元,进程的解释如下:

Erlang进程是轻量级进程,它的生成、上下文切换和消息传递是由虚拟机管理的。操作系统线程的Erlang进程之间没有任何联系,这使并发有关的操作不仅独立于底层的操作系统,而且也是非常高效和具有很强可扩展性。”

由此可见,Erlang里的进程跟操作系统里常提到的进程,线程完全没有关系,只是Erlang并发机制里基本并发单元的一个代称。下面详细的说明Erlang进程的创建过程。

Erlang虚拟机的角度来看,Erlang进程就是一个process结构,定义在$OTP_SRC/erts/emulator/beam/erl_process.h中(struct process),该结构中包含进程所使用的堆栈、GC、调度、消息队列等信息。Erlang程序通过erlang:spawn或者相关BIF调用(spawn_optspawn_link等)可以生成一个新的进程。

 

erts中,spawn调用会最终调用到spawn_3

[$OTP_SRC/erts/emulator/beam/bif.c --> spawn_3]

pid = erl_create_process(BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, &so);

首先就是调用erl_create_process来生成一个新的进程,会返回创建进程的pid。我们来看看erl_create_process具体都做了哪些事情。

 

[$OTP_SRC/erts/emulator/beam/erl_process.c --> erl_create_process]

// 参数说明
Eterm
erl_create_process(Process* parent, /* Parent of process (default group leader). */
		   Eterm mod,	/* Tagged atom for module. */
		   Eterm func,	/* Tagged atom for function. */
		   Eterm args,	/* Arguments for function (must be well-formed list). */
		   ErlSpawnOpts* so) /* Options for spawn. */

	// 首先调用alloc_process分配一个process结构需要的内存空间
p = alloc_process(); /* All proc locks are locked by this thread on success */
if (!p) {
	// 如果p==null,则说明系统中进程数量已达到上限
		so->error_code = SYSTEM_LIMIT;
		goto error;
}
    /* Scheduler queue mutex should be locked when changeing
     * prio. In this case we don't have to lock it, since
     * noone except us has access to the process.
     */
    
	// 设置process的最小堆大小
if (so->flags & SPO_USE_ARGS) {
//以参数值设置堆属性,一般通过erlang:spawn_opt调用传入参数
		p->min_heap_size  = so->min_heap_size; // 最小堆内存
		p->min_vheap_size = so->min_vheap_size; // 最小虚拟堆内存
//(用于存放二进制数据)
		p->prio           = so->priority; // 进程优先级(max, high, normal, low)
		p->max_gen_gcs    = so->max_gen_gcs; // full gc之前可进行的minor gc的最大次数
} else {
	// 按默认值设置:H_MIN_SIZE=233 (fib(11),erlang里的堆内存按照fib系列增长,
	// 具体可参见[$OTP_SRC/erts/emulator/beam/erl_c.c --> erts_init_gc]里的说明)
		p->min_heap_size  = H_MIN_SIZE;
		p->min_vheap_size = BIN_VH_MIN_SIZE; // 默认值32768(216)
		p->prio           = PRIORITY_NORMAL;
		p->max_gen_gcs    = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
    }
    
    // 创建进程时传入的module,function,和参数的数量
    p->initial[INITIAL_MOD] = mod;
    p->initial[INITIAL_FUN] = func;
p->initial[INITIAL_ARI] = (Uint) arity;
    
/*
     * Must initialize binary lists here before copying binaries to process.
     */
    p->off_heap.first = NULL;
    p->off_heap.overhead = 0;

    // 计算初始需要的heap大小
heap_need +=
		IS_CONST(parent->group_leader) ? 0 : NC_HEAP_SIZE(parent->group_leader);

    if (heap_need < p->min_heap_size) {
		sz = heap_need = p->min_heap_size;
} else {
	/*
 	 * Find the next heap size equal to or greater than the given size (if offset == 0).
 	 *
 	 * If offset is 1, the next higher heap size is returned (always greater than size).
 	*/
		sz = erts_next_heap_size(heap_need, 0);
}

// 分配进程堆内存
    p->heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*sz);
p->old_hend = p->old_htop = p->old_heap = NULL;
// 进程堆内存里年轻代的标志位:地址小于此标志位的,是较老的年轻代(一般情况
// 下,这些对象至少经过了一次minor gc或者major gc);大于这个地址的是较年轻的
// 年轻代。
p->high_water = p->heap;
	// minor gc的次数
p->gen_gcs = 0;
// 栈顶,紧邻堆
    p->stop = p->hend = p->heap + sz;
    p->htop = p->heap;
    p->heap_sz = sz;

    /* No need to initialize p->fcalls. */
	// 当前模块及函数信息
    p->current = p->initial+INITIAL_MOD;

	// 第一条指令设置为i_apply 
p->i = (BeamInstr *) beam_apply;
// cp保存进入一个函数调用时,当前函数的下一条指令 
    p->cp = (BeamInstr *) beam_apply+1;

	// 消息队列
    p->msg.first = NULL;
    p->msg.last = &p->msg.first;
    p->msg.save = &p->msg.first;
    p->msg.len = 0;
#ifdef ERTS_SMP
    // 消息进入队列
p->msg_inq.first = NULL;
    p->msg_inq.last = &p->msg_inq.first;
    p->msg_inq.len = 0;
p->bound_runq = NULL;

if (so->flags & SPO_LINK) {
	// 进程链接,由spawn_link指定
		if (IS_TRACED_FL(parent, F_TRACE_PROCS)) {
	    	trace_proc(parent, parent, am_link, p->id);
		}
		// 父进程及当前进程互相连接
		erts_add_link(&(parent->nlinks), LINK_PID, p->id);
		erts_add_link(&(p->nlinks), LINK_PID, parent->id);
	}

    /*
     * Test whether this process should be initially monitored by its parent.
     */
    if (so->flags & SPO_MONITOR) {
		Eterm mref;
		//进程监控:单向,由spawn_monitor指定
		mref = erts_make_ref(parent);
		erts_add_monitor(&(parent->monitors), MON_ORIGIN, mref, p->id, NIL);
		erts_add_monitor(&(p->monitors), MON_TARGET, mref, parent->id, NIL);
		so->mref = mref;
}
    /*
     * Schedule process for execution.
     */
if (!((so->flags & SPO_USE_ARGS) && so->scheduler))
	// 如果参数中未指定scheduler,则使用父进程的任务队列
		rq = erts_get_runq_proc(parent);
else {
	// 根据绑定的scheduler,获取任务队列(spawn_opt中可以绑定scheduler,文档
	// 中无说明,具体可见:[$OTP_SRC/erts/emulator/beam/bif.c --> spawn_opt_1])
		int ix = so->scheduler-1;
		ASSERT(0 <= ix && ix < erts_no_run_queues);
		rq = ERTS_RUNQ_IX(ix);
		p->bound_runq = rq;
    }

#ifdef ERTS_SMP
	// 设置当前进程的任务队列
    p->run_queue = rq;
#endif
	// 设置进程的状态为waiting
p->status = P_WAITING;
// 将当前进程添加到任务队列,并将进程的状态设置为runnable
    notify_runq = internal_add_to_runq(rq, p);
	// 唤醒调度器
    smp_notify_inc_runq(notify_runq);
	// 返回进程PID
    res = p->id;

	// 创建成功
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id));

进程创建成功后,会将pid返回到spawn_3调用。

 

[$OTP_SRC/erts/emulator/beam/bif.c --> spawn_3]

	if (ERTS_USE_MODIFIED_TIMING()) {
	    BIF_TRAP2(erts_delay_trap, BIF_P, pid, ERTS_MODIFIED_TIMING_DELAY);
	}
	BIF_RET(pid);

BIF_TRAP2Erlang里的Trap机制,关于Trap机制的详细说明见这里。这里的调用属于第三类,主动放弃CPUerts_delay_trap最终会以以pidERTS_MODIFIED_TIMING_DELAY()为参数调用erlang:delay_trapERTS_USE_MODIFIED_TIMING()这个宏成立的条件是modified timing开关打开,具体参数erl+T参数,默认未打开。更详细的说明见这里

 

[$OTP_SRC/ erts/preloaded/src /erlang.erl --> erlang:delay_trap]

%%
%% Trap function used when modified timing has been enabled.
%%
delay_trap(Result, 0) -> erlang:yield(), Result;
delay_trap(Result, Timeout) -> receive after Timeout -> Result end. 
erlang:yield等同于receive after 1 -> Result enddelay_trap的作用就是让当前进程放弃CPU,使其它的进程有机会运行,在spawn调用的场景下,也就是会使新创建的进程有机会被调度到。 
  • 大小: 92 KB
  • 大小: 81.3 KB
1
0
分享到:
评论
1 楼 AsIMovedon 2013-04-10  
很好,通俗易懂。学习了~

相关推荐

    erlang-23.2.3-1.el7.x86_64.rpm和erlang-23.2.1-1.el7.x86_64.rpm.rar

    Erlang是一种高级编程语言,特别适用于并发、分布式和实时计算系统。它的设计目标是创建一个高度可靠且容错的环境,广泛应用于电信、金融、在线游戏和消息队列服务等领域。Erlang由爱立信开发,其核心概念包括进程...

    erlang-22.3-1.el7.x86_64.rpm

    1. **并发**:Erlang的轻量级进程和并行执行使得处理大量并发连接变得简单。 2. **热代码替换**:允许在运行时更新代码,无需停止系统服务,这对持续运行的服务如RabbitMQ非常有用。 3. **分布式**:Erlang节点可以...

    erlang-18.3-1.el7.centos.x86_64.zip

    Erlang之所以成为RabbitMQ的首选语言,是因为其内置的并发机制、分布式特性以及错误恢复能力。Erlang的BEAM虚拟机(Erlang虚拟机)允许在单个进程中创建大量轻量级线程,称为进程,这些进程之间的通信高效且易于实现...

    erlang-19.0.4-1.el7.centos.x86_64.zip

    Erlang是一款强大的编程语言,尤其在分布式计算、并发处理和实时...Erlang的特性如轻量级进程、消息传递和错误恢复机制使得它在构建高可用性、容错性强的系统中备受青睐,特别是在电信、互联网服务和大数据处理等领域。

    erlang-rpm-21.3.4.zip

    1. **轻量级进程**:Erlang中的进程非常轻便,消耗资源少,可以实现大量并发处理,适合构建高并发、高可用性的系统。 2. **分布式计算**:Erlang节点间可以通过网络进行通信,允许在多台机器上分布运行程序,提高了...

    erlang-19.0.4-1.el7.centos.x86_64.rpm

    这种并发机制使得Erlang在处理高并发场景时表现出色,如大规模在线服务和实时通信系统。 2. **分布式**:Erlang天生支持分布式计算,可以在多台机器上无缝运行,节点间通信高效而可靠,为构建分布式系统提供了便利...

    erlang-18.3.4.7-1.el6.x86_64.rpm

    ● 并发性 - Erlang支持超大量级的并发进程,并且不需要操作系统具有并发机制。 ● 分布式 - 一个分布式Erlang系统是多个Erlang节点组成的网络(通常每个处理器被作为一个节点) ● 健壮性 - Erlang具有多种基本的...

    Erlang-or-java.rar_erlang

    Erlang是一种面向并发、分布式、容错的编程语言,常用于构建高可用性和高并发性的系统,而Java则是一种广泛应用的通用编程语言,广泛应用于企业级应用开发。描述中提到该示例已经过测试且易于理解,这意味着我们将...

    erlang-19.0.4-1.el7.centos.x86_64.rar

    在Erlang 19.0.4中,开发者可以利用其强大的并发特性来构建高可用性系统,因为Erlang的进程模型允许数千个轻量级进程在同一时间运行。此外,Erlang虚拟机(BEAM)提供了垃圾回收机制,确保了内存管理的效率。Erlang...

    Erlang-OTP-API 离线查询英文全手册

    7. **并发和进程管理**:Erlang的并发模型基于轻量级进程(LWP),手册会讲解如何创建、通信和管理进程,以及如何利用`spawn`、`send`、`receive`等机制。 8. **性能和监控**:通过`observer`工具和其他监控模块,...

    最新版erlang-23.3.4.3-1.el7.x86_64.rpm(CentOS7)

    Erlang是一种高级编程语言,特别为并发、分布式计算和容错设计,广泛应用于网络通信、实时系统和大型分布式计算环境中。最新版的Erlang是23.3.4.3-1.el7.x86_64.rpm,这个版本针对CentOS 7进行了优化。Erlang以其轻...

    2018-FL+erlang语言-Functional Federated Learning in Erlang (ffl-er

    1. 并发与分布式计算:ffl-erl充分利用Erlang的轻量级进程和消息传递机制,实现高效的并发处理。这使得在多客户端环境中,模型的同步和更新过程可以高效且低延迟地进行。 2. 功能性编程:Erlang的函数式编程风格...

    erlang-examples-2.0.pdf

    Erlang是一种为构建并发和分布式系统设计的编程语言,它特别适用于需要高可靠性和高可用性的场景,例如电信、银行和即时通讯系统等。这份文档通过一系列例子详细展示了Erlang语言的核心概念,如进程通信、模式匹配、...

    Erlang-OTP-19.0.zip

    它通过轻量级进程(Lightweight Processes, LWP)实现并发,这些进程消耗的资源很少,使得系统能同时运行大量进程而不会导致性能下降。 2. **分布式计算**:Erlang OTP支持跨节点的分布式计算,使得开发者可以构建...

    Concurrent Programming in ERLANG (P1-90)

    进程间通信是Erlang并发模型的核心部分。进程之间通过发送和接收消息来协调工作。每个进程都有一个消息队列,当进程接收到消息时,它可以决定如何响应。 **5.3 超时** 超时机制允许进程在等待消息时设置时间限制。...

    Erlang-game-server开发实践.zip

    Erlang的并发模型基于轻量级进程,它们之间的通信通过消息传递完成。这种模型非常适合游戏服务器,因为游戏通常涉及大量并发用户交互。同时,Erlang的分布式特性允许我们在多台机器上无缝地扩展游戏服务器,以应对高...

    对Erlang R11B-5 ssl库的修正

    - Erlang OTP(开放电信平台)是一套设计模式和库,用于构建高度并发、容错的系统。`ssl`库作为OTP的一部分,其修改可能遵循了OTP的设计原则,如进程间通信(IPC)、故障恢复和分布式系统的可靠性。 3. **SSL/TLS...

    otp_src_R11B-5.tar.gz_OTP_erlang_otp-src-R11B_otp_s

    Erlang的并发模型基于进程,每个进程都有自己的堆栈和局部变量,进程间通过异步消息传递进行通信,这样的设计保证了高并发场景下的低延迟和高效率。 OTP框架在Erlang之上提供了一系列库、设计原则和开发工具,用于...

    erlang并发编程 .pdf

    在《Erlang并发编程》一书中,详细介绍了Erlang语言的核心并发模型和编程范式,包括但不限于串行编程、进程创建、进程间通信、分布式编程以及错误处理等。书中指出Erlang的并发性能得益于其轻量级进程,这些进程是由...

Global site tag (gtag.js) - Google Analytics