Usando Process.spawn como reemplazo de Process.fork

Mi entorno de desarrollo es una máquina Windows que ejecuta ruby ​​1.9.3p125 (RubyInstaller) y Rails 3.2.8.

Un problema que surge, una y otra vez, al usar gems de terceros, es la falta de fork () en Windows. Esto ha obstaculizado recientemente mi capacidad de usar casi cualquier gem de prueba distribuida (como estas ), debido a su dependencia de la horquilla.

Algunas preguntas anteriores sobre StackOverflow han intentado encontrar una solución a este mismo problema, pero antes de agregar Process.spawn en ruby, o eran de personas obligadas a usar una versión anterior de Ruby, por alguna otra razón.

Una de las soluciones propuestas es utilizar Cygwin para obtener soporte de fork (), lo cual está fuera de cuestión para esto – creo que preferiría cambiar a Linux por completo, antes de eso.

Otra solución propuesta ha sido utilizar la gem win32-process para obtener el soporte fork (). El soporte de la horquilla se eliminó de la versión más reciente (0.7.0) y, al utilizar la siguiente versión más antigua (0.6.6), la horquilla de soporte (clasificación) no parece funcionar, al menos para ejecutar cualquiera de las distribuciones probando las gems que he probado (pruebas de Spork, Parallel, Hydra, Specjour, prácticamente todas). Curiosamente, el autor de la gem alude, en el archivo Léame, a Process.spawn como una solución aceptable para Process.fork.

He visto mucha información que implica o que indica que el spawn se puede usar como reemplazo de la bifurcación, en Windows, con Ruby 1.9. He pasado una buena cantidad de tiempo jugando con esto, básicamente tratando de reemplazar Process.fork con Process.spawn en varias de las gems referenciadas, sin éxito. Me parece que tal vez el comportamiento sea similar, pero no exactamente el mismo. Por ejemplo, no está claro si spawn realmente copia todo el proceso de la misma manera que la horquilla, o simplemente crea un nuevo proceso con los argumentos suministrados. Tampoco está claro si el método spawn incluso acepta otro método Ruby como argumento, o solo un comando del sistema. Los documentos parecen implicar que es solo un comando, pero un método parece funcionar (tipo de), pero puedo estar haciendo cosas incorrectamente. Creo que para algunas cosas, el tenedor solo se usó para crear un “hilo barato”, en versiones anteriores de ruby ​​que no admitían el enhebrado. Sin embargo, parece que estas gems de prueba distribuidas pueden confiar legítimamente en la funcionalidad completa de fork (), para mantener el estado del proyecto, y para no cargar todo el entorno ruby ​​para cada prueba. Esto está un poco fuera de mis deberes de progtwigción y experiencia normales, así que puedo estar haciendo algunas suposiciones incorrectas.

Entonces, mi pregunta es, ¿se puede usar Process.spawn de forma relativamente simple para lograr el mismo resultado que Process.fork, en todos los casos? Estoy empezando a sospechar que no, pero si es así, ¿podría alguien publicar un ejemplo de cómo se haría la transformación?

EDITAR : Hay un caso de uso común de fork() que puede reemplazarse con spawn() – the fork()exec() combo. Muchas aplicaciones UNIX más antiguas (y modernas), cuando quieren generar otro proceso, primero se bifurcan y luego hacen una llamada exec (el exec reemplaza el proceso actual con otro). Esto realmente no necesita fork() , por lo que puede reemplazarse con spawn() . Así que esto:

 if(!fork()) exec("dir") end 

puede ser reemplazado por:

 Process.spawn("dir") 

Si alguna de las gems usa fork() como esta, la solución es fácil. De lo contrario, es casi imposible.


EDITAR : La razón por la cual la implementación de fork() win32-process no funciona (por lo que puedo decir de los documentos), básicamente es spawn() , que no es fork() en absoluto.


No, no creo que se pueda hacer. Verá, Process.spawn crea un nuevo proceso con el estado en blanco predeterminado y el código nativo. Por lo tanto, aunque puedo hacer algo como Process.spawn('dir') se iniciará un nuevo proceso en blanco ejecutando dir , que no clonará ningún estado del proceso actual. Solo la conexión a su progtwig es la conexión padre – hijo.

Usted ve, fork() es una llamada de muy bajo nivel. Por ejemplo, en Linux, lo que fork() básicamente hace es esto: primero, se crea un nuevo proceso con un estado de registro exactamente clonado. Luego, Linux hace una referencia de copia sobre escritura a todas las páginas del proceso principal. Linux luego clona algunos otros indicadores de proceso. Obviamente, todas estas operaciones solo pueden ser realizadas por el kernel, y el kernel de Windows no tiene las facilidades para hacerlo (y tampoco se pueden parchear).

Técnicamente, solo los progtwigs nativos necesitan el SO para algún tipo de soporte tipo fork() . Cualquier capa de código necesita la cooperación de la capa superior para hacer algo como fork() . Entonces, aunque el código C nativo necesita la cooperación del kernel para bifurcarse, en teoría, Ruby solo necesita la cooperación del intérprete para hacer un fork. Sin embargo, el intérprete de Ruby no tiene una función de instantánea / restauración, que sería necesariamente implementar un tenedor. Debido a esto, la bifurcación normal de Ruby se logra bifurcando al intérprete en sí mismo, no al progtwig Ruby.

Entonces, si pudiera parchar el intérprete de Ruby para agregar una función detener / iniciar e instantánea / restaurar, podría hacerlo, pero ¿no es así? No lo creo.

entonces cuales son tus opciones? Esto es lo que puedo pensar:

  • Parchear el intérprete Ruby
  • Parchee el código que usa fork() para tal vez usar hilos o engendrar
  • Obtenga UNIX (sugiero este)
  • Utilice Cygwin

Edición 1: No recomendaría utilizar la horquilla de Cygwin, ya que implica tablas de procesos Cygwin especiales, no hay copia en escritura, lo que la hace muy ineficiente. Además, implica un montón de saltos y una gran cantidad de copias. Evítalo si es posible. Además, debido a que Windows no proporciona instalaciones para copiar espacios de direcciones, es muy probable que las bifurcaciones fallen, y pasarán la mayor parte del tiempo (ver aquí ).