Erlang进程与内核线程有何关系?如果有的话,它们如何映射?

48

Erlang 以支援多个轻量级进程而闻名;它能够做到这一点,是因为这些进程不是传统意义上的进程,甚至不像 P-threads 中的线程那样,而是完全在用户空间中的线程。

这很好(实际上非常棒)。但是,在多核/多处理器环境下,Erlang 线程如何并行执行呢?它们肯定必须以某种方式映射到内核线程才能在单独的核心上执行,对吗?

假设确实是这样,那么这是如何完成的?许多轻量级进程被映射到一个内核线程吗?

还是有其他解决方法?

4个回答

65

答案取决于使用的虚拟机:

1) 非SMP: 有一个调度程序(操作系统线程),它执行来自可运行进程池(即那些没有被例如receive阻塞的进程)的所有Erlang进程。

2) SMP: 有K个调度程序(操作系统线程,K通常是CPU核心数),从共享进程队列中执行Erlang进程。它是一个简单的FIFO队列(通过锁允许多个操作系统线程同时访问)。

3) R13B及更高版本的SMP:将会有K个调度程序(与之前一样),它们从多个进程队列中执行Erlang进程。每个调度程序都有自己的队列,因此将添加进程迁移逻辑,以便从一个调度程序迁移到另一个调度程序。这种解决方案通过避免在共享进程队列中过多地锁定来提高性能。

有关更多信息,请参见由肯尼斯·伦丁(Kenneth Lundin)、爱立信公司为Erlang用户大会(2008年11月13日,斯德哥尔摩)准备的此文档


11

我想修正之前的答案。

Erlang, 或者说 Erlang 运行时系统(erts),默认将调度器数量(操作系统线程)和运行队列数量设置为您平台上的处理元素数量。这是处理器核心或硬件线程数。您可以使用以下方法在运行时更改这些设置:

erlang:system_flag(schedulers_online, NP) -> PrevNP

Erlang进程目前没有与任何调度程序相关联。将进程在调度器之间平衡的逻辑遵循两个规则:1)饥饿的调度器会从另一个调度器中夺取工作。2)建立迁移路径,将进程从具有大量进程的调度器推送到具有较少工作量的调度器。这样做是为了确保每个进程的减少计数(执行时间)公平。

但是调度程序可以锁定到特定的处理元素上。默认情况下不会执行此操作。要让erts执行调度程序->核心关联,请使用:

erlang:system_flag(scheduler_bind_type, default_bind) -> PrevBind

文档中还可以找到其他绑定类型。使用亲和力可以在高负载情况下大大提高性能!尤其是在高锁争用情况下。此外,至少可以说,Linux内核无法处理超线程。如果您的平台有超线程,您应该真正使用Erlang中的此功能。


1
我想对已接受答案中的内容进行补充。
Erlang调度程序是Erlang运行时系统的基本部分,提供了自己的抽象和实现,将轻量级进程概念置于OS线程之上。
每个调度程序在单个OS线程内运行。通常,调度程序与硬件CPU(核心)一样多(可以配置),当调度程序数量超过硬件核心数时,自然不会带来太多价值。该系统还可以配置调度程序不会在OS线程之间跳转。
现在,当Erlang进程被创建时,完全由ERTS和调度程序负责管理其生命周期和资源消耗以及其内存占用等。
其中一个核心实现细节是,当调度程序从运行队列中选择该进程时,每个进程都有可用的2000个还原时间预算。系统中的每个进程(甚至I/O)都保证拥有还原预算。这实际上使ERTS成为具有抢占式多任务处理能力的系统。

我推荐Jesper Louis Andersen在这个主题上的一篇博客文章http://jlouisramblings.blogspot.com/2013/01/how-erlang-does-scheduling.html

简而言之,Erlang进程不是操作系统线程,并且不能直接映射到它们。Erlang调度器是在操作系统线程上运行的,提供了更细粒度的Erlang进程的智能实现,将这些细节隐藏在程序员眼中。


1
我这里只是猜测,但我想线程数应该很少,从一个共同的进程池中选择进程进行执行。一旦进程遇到阻塞操作,执行它的线程将其置于一旁并选择另一个进程。当正在执行的进程导致另一个进程变得不受阻时,该新解除阻塞的进程会被放回到池中。我想一个线程在某些点上甚至可以停止执行进程,以便为其他进程提供服务。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接