Erlang产生了大量的C进程

时间:2015-06-07 16:54:06

标签: c multithreading lua erlang ffi

我一直在研究如何在Erlang中嵌入语言(让我们以Lua为例)。这当然不是一个新想法,并且有许多库可以做到这一点。但是我想知道是否有可能启动一个由Lua修改的状态的Genserver。这意味着一旦启动Genserver,它将启动一个(长时间运行的)Lua进程来操纵Genserver的状态。我知道这也是可能的,但我想知道我是否可以产生1,000 10,000甚至100,000个这些过程。

我对这个话题并不熟悉,但我做了一些研究。 (如果我对这些选项中的任何一个错误,请纠正我。)

TLDR;跳到最后一段。

第一个选项:NIF:

这似乎不是一个选项,因为它会阻止当前进程的Erlang Scheduler。如果我想生成大量的这些,它将冻结整个运行时。

第二个选项:端口驱动程序:

它类似于NIF,但通过将数据发送到指定端口进行通信,该端口也可以将数据发送回Erlang。这很好,虽然这似乎也阻止了调度程序。我已经尝试了一个为你做锅炉平台的库,但这似乎在产生10个进程后阻止了调度程序。我也查看了Erlang文档中的postgresql示例,据说这是异步但我无法获得示例代码(R13?)。是否可以在不阻塞运行时的情况下运行尽可能多的端口驱动程序进程?

第三个选项:C节点:

我认为这很有意思并想尝试一下,但显然该项目" erlang-lua"已经这样做了。它很好,因为如果出现问题并且进程被隔离,它就不会使你的Erlang VM崩溃。但是为了实际生成单个进程,您需要生成整个节点。我不知道这有多贵。我也不确定连接集群中节点的限制是什么,但我不认为自己会产生100,000个C节点。

第四个选项:端口:

起初我认为这与端口驱动程序相同,但它实际上是不同的。您生成一个执行应用程序并通过STDIN和STDOUT进行通信的进程。这适用于产生大量进程,并且(我认为?)它们不会对Erlang VM构成威胁。但是,如果我要通过STDIN / STDOUT进行通信,为什么还要开始使用嵌入式语言呢?不妨使用任何其他脚本语言。

因此,经过对某个领域的大量研究,我对此并不熟悉。您可以将Genserver作为"实体" AI是用Lua编写的。这就是为什么我想为每个实体制定流程的原因。我的问题是如何实现产生许多与长期运行的Lua进程通信的Genservers?这甚至可能吗?我应该以不同的方式处理我的问题吗?

1 个答案:

答案 0 :(得分:5)

如果您可以制作Lua代码 - 或者更准确地说,它的底层本机代码 - 与Erlang VM合作,那么您有几个选择。

考虑Erlang VM最重要的功能之一:在相对较小的调度程序线程集上管理(可能是大量的)Erlang轻量级进程的执行。它使用几种技术来了解进程何时耗尽其时间片或正在等待,因此应安排出来以使另一个进程有机会运行。

您似乎在问如何在VM中运行本机代码,但是正如您已经暗示的那样,本机代码可能导致VM出现问题的原因是它没有实际的停止方法本机代码完全接管调度程序线程,从而阻止正常的Erlang进程执行。因此,本机代码必须合作地将调度程序线程返回给VM。

对于较旧的NIF,此类合作的选择是:

  1. 将NIF调用在调度程序线程上运行的时间保持为1毫秒或更短。
  2. 创建一个或多个私有线程。将每个长时间运行的NIF调用从其调度程序线程转移到专用线程执行,然后将调度程序线程返回给VM。
  3. 这里的问题是并非所有调用都能在1ms或更短的时间内完成,并且管理私有线程可能容易出错。为了解决第一个问题,一些开发人员会将工作分解成块并使用Erlang函数作为包装来管理一系列简短的NIF调用,每个调用完成一部分工作。至于第二个问题,好吧,有时你无法避免它,尽管它固有的困难。

    在Erlang 17.3或更高版本上运行的NIF也可以使用enif_schedule_nif function合作生成调度程序线程。要使用此功能,本机代码必须能够以块的形式完成其工作,以便每个块可以在通常的1ms NIF执行窗口内完成,类似于前面提到的方法,但不需要人为地返回到Erlang包装器。我的bitwise example code提供了很多有关此内容的详细信息。

    Erlang 17还带来了一个实验性功能,默认情况下关闭,称为脏调度程序。这是一组VM调度程序,它们与常规调度程序不具有相同的本机代码执行时间限制;在那里工作可以阻止基本上无限期而不会中断正常的VM操作。

    脏调度程序有两种形式:用于CPU绑定工作的CPU调度程序和用于I / O绑定工作的I / O调度程序。在编译为启用脏调度程序的VM中,默认情况下存在与常规调度程序一样多的脏CPU调度程序,并且有10个I / O调度程序。可以使用命令行开关更改这些数字,但请注意,为了防止常规调度程序不足,您永远不会有比常规调度程序更脏的CPU调度程序。应用程序使用前面提到的相同enif_schedule_nif函数在脏调度程序上执行NIF。我的bitwise example code也提供了很多有关此内容的详细信息。肮脏的调度程序也将成为Erlang 18的实验性功能。

    链接式端口驱动程序中的本机代码与NIF具有相同的调度程序执行时间限制,但驱动程序具有NIF不具备的两个功能:

    1. 驱动程序代码可以将文件描述符注册到VM轮询子系统中,并在任何这些文件描述符变为I / O就绪时收到通知。
    2. 驱动程序API支持访问非调度程序异步线程池,其大小可配置,但默认情况下有10个线程。
    3. 第一个功能允许本机驱动程序代码避免阻塞线程进行I / O.例如,驱动程序代码可以注册套接字文件描述符,而不是执行阻塞recv调用,以便VM可以轮询它并在文件描述符可读时调用驱动程序。

      第二个功能提供了一个单独的线程池,可用于不符合调度程序线程本机代码执行时间约束的驱动程序任务。您可以在NIF中实现相同的功能,但您必须设置自己的线程池并编写自己的本机代码来管理和访问它。但无论您是使用驱动程序异步线程池,您自己的NIF线程池还是脏调度程序,请注意它们都是常规操作系统线程,因此尝试启动其中的大量线程是不切实际的。

      本机驱动程序代码尚未具有脏调度程序访问权限,但此工作正在进行中,并且可能在18.x版本中作为实验性功能提供。

      如果您的Lua代码可以使用这些功能中的一个或多个来与Erlang VM配合使用,那么您正在尝试的是可能的。