sistema procesos proceso operativo hijo funcion entre comunicacion c multithreading lua erlang ffi

procesos - proceso padre e hijo sistema operativo



Erlang engendrando grandes cantidades de procesos C (1)

He estado investigando cómo podría incrustar idiomas (usemos Lua como ejemplo) en Erlang. Esto, por supuesto, no es una idea nueva y hay muchas bibliotecas que pueden hacer esto. Sin embargo, me preguntaba si era posible iniciar un Genserver con estado modificado por Lua. Esto significa que una vez que inicie Genserver, comenzará un proceso Lua (de larga duración) para manipular el estado del Genserver. Sé que esto también es posible, pero me preguntaba si podría generar 1,000 10,000 o incluso 100,000 de estos procesos.

No estoy muy familiarizado con este tema, pero investigué un poco. (Por favor corrígeme si me equivoco con alguna de estas opciones).

TLDR; Pase al último párrafo.

Primera opción: NIF:

Esto no parece una opción, ya que bloqueará el Programador de Erlang del proceso actual. Si quiero generar una gran cantidad de estos, se congelará todo el tiempo de ejecución.

Segunda opción: controlador de puerto:

Es como un NIF, pero se comunica enviando datos a un puerto específico, que también puede enviar datos a Erlang. Esto es bueno, aunque esto también parece bloquear el programador. He intentado una biblioteca que también hace la plataforma de calderas para usted, pero eso pareció bloquear el programador después de generar 10 procesos. También busqué en el ejemplo de postgresql en la Documentación de Erlang que se dice que es asíncrono pero no pude obtener el código de ejemplo para trabajar (R13?). ¿Es posible ejecutar tantos procesos Port Driver sin bloquear el tiempo de ejecución?

Tercera opción: Nodos C:

Pensé que esto era muy interesante y quería probarlo, pero aparentemente el proyecto "erlang-lua" ya lo hace. Es agradable porque no bloqueará tu máquina virtual Erlang si algo sale mal y los procesos están aislados. Pero para generar un proceso único, debes generar un nodo completo. No tengo idea de lo caro que es esto. Tampoco estoy seguro de cuál es el límite para conectar nodos en un clúster, pero no me veo generando 100.000 nodos C.

Cuarta opción: Puertos:

Al principio pensé que esto era lo mismo que un controlador de puerto, pero en realidad es diferente. Genera un proceso que ejecuta una aplicación y se comunica a través de STDIN y STDOUT. Esto funcionaría bien para generar una gran cantidad de procesos, y (¿no?) No son una amenaza para la máquina virtual de Erlang. Pero si me voy a comunicar a través de STDIN / STDOUT, ¿para qué empezar con un lenguaje incrustable? También podría usar cualquier otro lenguaje de scripting.

Entonces, después de mucha investigación en un campo que no conozco, he llegado a esto. Podría un Genserver como una "entidad" donde la IA está escrita en Lua. Por eso me gustaría tener un proceso para cada entidad. Mi pregunta es ¿cómo logro generar muchos Genservers que se comunican con procesos Lua de larga ejecución? ¿Esto es posible? ¿Debería abordar mi problema de manera diferente?


Si puede hacer que el código Lua, o más exactamente, su código nativo subyacente, coopere con la VM Erlang, tiene algunas opciones.

Considere una de las funciones más importantes de la VM de Erlang: administrar la ejecución de un (potencialmente gran número de) procesos livianos de Erlang en un conjunto relativamente pequeño de hilos del planificador. Utiliza varias técnicas para saber cuándo un proceso ha agotado su segmento de tiempo o lo está esperando, por lo que debe programarse para que otro proceso tenga la oportunidad de ejecutarse.

Parece que se pregunta cómo puede ejecutar el código nativo como guste dentro de la VM, pero como ya ha insinuado, la razón por la cual el código nativo puede causar problemas para la VM es que no tiene forma práctica de detener el código nativo de tomar completamente el hilo de un programador y así evitar que los procesos regulares de Erlang se ejecuten. Debido a esto, el código nativo tiene que ceder cooperativamente el hilo del planificador a la VM.

Para las NIF antiguas, las opciones para dicha cooperación fueron:

  1. Mantenga la cantidad de tiempo que las llamadas NIF se ejecutan en una secuencia de programador en 1 ms o menos.
  2. Crea uno o más hilos privados. Transición de cada llamada NIF de larga ejecución desde su hilo del planificador a un hilo privado para su ejecución, luego devuelva el hilo del planificador a la VM.

Los problemas aquí son que no todas las llamadas pueden completarse en 1 ms o menos, y que la gestión de subprocesos privados puede ser propensa a errores. Para evitar el primer problema, algunos desarrolladores dividían el trabajo en fragmentos y usaban una función de Erlang como envoltorio para administrar una serie de llamadas NIF cortas, cada una de las cuales completaba una porción de trabajo. En cuanto al segundo problema, bueno, a veces no puedes evitarlo, a pesar de su dificultad inherente.

Los NIF que se ejecutan en Erlang 17.3 o posterior también pueden producir cooperativamente el hilo del planificador utilizando la función enif_schedule_nif . Para usar esta característica, el código nativo debe poder hacer su trabajo en fragmentos de manera que cada fragmento pueda completarse dentro de la ventana de ejecución de NIF habitual de 1 ms, similar al enfoque mencionado anteriormente, pero sin la necesidad de regresar artificialmente a un wrapper de Erlang. Mi código de ejemplo bit a bit proporciona muchos detalles sobre esto.

Erlang 17 también trajo una función experimental, desactivada por defecto, llamada programadores sucios . Este es un conjunto de programadores de VM que no tienen las mismas restricciones de tiempo de ejecución de código nativo que los planificadores regulares; el trabajo allí puede bloquearse durante períodos esencialmente infinitos sin interrumpir el funcionamiento normal de la máquina virtual.

Los programadores sucios vienen en dos formas: programadores de CPU para trabajo vinculado a CPU y programadores de E / S para trabajo vinculado a E / S. En una máquina virtual compilada para habilitar programadores sucios, hay por defecto tantos programadores sucios de CPU como programadores regulares, y hay 10 programadores de E / S. Estos números pueden modificarse utilizando los modificadores de la línea de comandos, pero tenga en cuenta que para evitar el agotamiento del planificador habitual, nunca podrá tener más programadores de CPU sucios que los programadores regulares. Las aplicaciones utilizan la misma función enif_schedule_nif mencionada anteriormente para ejecutar NIF en programadores sucios. Mi código de ejemplo bit a bit proporciona muchos detalles sobre esto también. Los programadores sucios seguirán siendo una característica experimental para Erlang 18 también.

El código nativo en los controladores de puertos enlazados está sujeto a las mismas restricciones de tiempo de ejecución del programador que los NIF, pero los controladores tienen dos características que los NIF no tienen:

  1. El código de controlador puede registrar descriptores de archivo en el subsistema de sondeo de VM y recibir una notificación cuando cualquiera de esos descriptores de archivo esté listo para I / O.
  2. El controlador API admite el acceso a un grupo de subprocesos asíncrono no planificador, cuyo tamaño es configurable pero de forma predeterminada tiene 10 subprocesos.

La primera característica permite que el código de controlador nativo evite bloquear un hilo para E / S. Por ejemplo, en lugar de realizar una llamada de bloqueo de recv , el código del controlador puede registrar el descriptor de archivo de socket para que la máquina virtual pueda sondearlo y llamar al controlador cuando el descriptor de archivo sea legible.

La segunda característica proporciona un grupo de subprocesos independiente útil para las tareas del controlador que no pueden ajustarse a las limitaciones de tiempo de ejecución del código nativo del subproceso del planificador. Puede lograr lo mismo en un NIF, pero debe configurar su propio grupo de subprocesos y escribir su propio código nativo para administrarlo y acceder a él. Pero independientemente de si usa el grupo de subprocesos asincrónicos del controlador, su propio grupo de subprocesos NIF o programadores sucios, tenga en cuenta que son todos los subprocesos normales del sistema operativo, por lo que tratar de iniciar un gran número de ellos simplemente no es práctico.

El código de controlador nativo todavía no tiene acceso sucio al programador, pero este trabajo está en curso y podría estar disponible como una característica experimental en una versión 18.x.

Si su código Lua puede hacer uso de una o más de estas características para cooperar con la máquina virtual Erlang, entonces lo que está intentando puede ser posible.