Les appels fork(2)
ralentissent car le processus parent utilise plus de mémoire en raison de la nécessité de copier les tables de pages. Dans de nombreuses utilisations courantes de fork(), où il est suivi par l'une des fonctions de la famille exec pour générer des processus enfants ( Kernel#system
, IO::popen
, Process::spawn
, etc.), il est possible de supprimer cette surcharge en utilisant des interfaces de génération de processus spéciales ( posix_spawn()
, vfork()
, etc.)
La bibliothèque posix-spawn vise à implémenter un sous-ensemble de l'interface Ruby 1.9 Process::spawn
d'une manière qui tire parti des interfaces de génération de processus rapides lorsqu'elles sont disponibles et fournit des solutions de repli sensées sur les systèmes qui ne le font pas.
Process::spawn
de Ruby 1.9 et des versions améliorées de Kernel#system
, Kernel#`
, etc. sous Ruby >= 1.8.7 (actuellement MRI uniquement).POSIX::Spawn::Child
de haut niveau pour des scénarios IPC rapides (mais corrects !) sans streaming. Les tests suivants illustrent le temps nécessaire pour créer/exécuter un processus enfant avec des tailles de mémoire résidentes croissantes sous Linux 2.6 et MacOS X. Les tests ont été exécutés à l'aide du programme posix-spawn-benchmark
inclus dans le package.
posix_spawn
est plus rapide que fork+exec
et s'exécute en temps constant lorsqu'il est utilisé avec POSIX_SPAWN_USEVFORK
.
fork+exec
est extrêmement lent pour les grands processus parents.
posix_spawn
est plus rapide que fork+exec
, mais aucun n'est affecté par la taille du processus parent.
Cette bibliothèque comprend deux interfaces distinctes : POSIX::Spawn::spawn
, une interface de génération de processus de niveau inférieur basée sur la nouvelle méthode Ruby 1.9 Process::spawn
, et POSIX::Spawn::Child
, une classe de niveau supérieur orientée vers une génération facile. de processus avec une gestion simple des flux d'entrées/sorties/d'erreurs standard basée sur des chaînes. Le premier est beaucoup plus polyvalent, le second nécessite beaucoup moins de code pour certains scénarios courants.
Le module POSIX::Spawn
(avec l'aide de l'extension C qui l'accompagne) implémente un sous-ensemble de l'interface Ruby 1.9 Process::spawn, en grande partie grâce à l'utilisation des interfaces systèmes IEEE Std 1003.1 posix_spawn(2)
. Ceux-ci sont largement pris en charge par divers systèmes d'exploitation UNIX.
Dans sa forme la plus simple, la méthode POSIX::Spawn::spawn
peut être utilisée pour exécuter un processus enfant similaire à Kernel#system
:
require 'posix/spawn'
pid = POSIX::Spawn::spawn('echo', 'hello world')
stat = Process::waitpid(pid)
La première ligne exécute echo
avec un seul argument et renvoie immédiatement le pid
du nouveau processus. La deuxième ligne attend la fin du processus et renvoie un objet Process::Status
. Notez que spawn
n'attend pas la fin de l'exécution du processus comme system
et ne récupère pas le statut de sortie de l'enfant - vous devez appeler Process::waitpid
(ou équivalent) ou le processus deviendra un zombie.
La méthode spawn
est capable d'effectuer un grand nombre d'opérations supplémentaires, de la configuration de l'environnement du nouveau processus à la modification du répertoire de travail de l'enfant, en passant par la redirection de descripteurs de fichiers arbitraires.
Consultez la documentation Ruby 1.9 Process::spawn
pour plus de détails et la section STATUS
ci-dessous pour un compte rendu complet des différentes fonctionnalités Process::spawn
prises en charge par POSIX::Spawn::spawn
.
system
, popen4
et `
En plus de la méthode spawn
, des implémentations compatibles Ruby 1.9 de Kernel#system
et Kernel#`
sont fournies dans le module POSIX::Spawn
. La méthode popen4
peut être utilisée pour générer un processus avec des objets stdin, stdout et stderr redirigés.
Le module POSIX::Spawn
peut également être mélangé à des classes et des modules pour inclure spawn
et toutes les méthodes utilitaires dans cet espace de noms :
require 'posix/spawn'
class YourGreatClass
include POSIX::Spawn
def speak(message)
pid = spawn('echo', message)
Process::waitpid(pid)
end
def calculate(expression)
pid, in, out, err = popen4('bc')
in.write(expression)
in.close
out.read
ensure
[in, out, err].each { |io| io.close if !io.closed? }
Process::waitpid(pid)
end
end
La classe POSIX::Spawn::Child
comprend une logique pour exécuter les processus enfants et lire/écrire à partir de leurs flux d'entrée, de sortie et d'erreur standard. Il est conçu pour prendre toutes les entrées dans une seule chaîne et fournit toutes les sorties sous forme de chaînes uniques. Il n'est donc pas bien adapté au streaming de grandes quantités de données entrantes et sortantes de commandes. Cela dit, cela présente certains avantages :
select(2)
) - gère tous les cas de blocage de canal en raison du dépassement des limites PIPE_BUF
sur un ou plusieurs flux. POSIX::Spawn::Child
prend les arguments spawn
standard lorsqu'il est instancié et exécute le processus jusqu'à son terme après avoir écrit toutes les entrées et lu toutes les sorties :
>> require 'posix/spawn'
>> child = POSIX::Spawn::Child.new('git', '--help')
Récupérez la sortie du processus écrite sur stdout / stderr ou inspectez l'état de sortie du processus :
>> child.out
=> "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]n ..."
>> child.err
=> ""
>> child.status
=> #<Process::Status: pid=80718,exited(0)>
Utilisez l'option :input
pour écrire des données sur le stdin du nouveau processus immédiatement après son apparition :
>> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
>> child.out
"42n"
Des options supplémentaires peuvent être utilisées pour spécifier la taille de sortie maximale ( :max
) et l'heure d'exécution ( :timeout
) avant l'abandon du processus enfant. Consultez la POSIX::Spawn::Child
pour plus d'informations.
POSIX::Spawn::Child.new
génère le processus immédiatement une fois instancié. Par conséquent, s'il est interrompu par une exception (soit en raison de l'atteinte de la taille de sortie maximale, du délai ou d'un autre facteur), il n'est pas possible d'accéder aux résultats out
ou err
car le constructeur n'a pas terminé.
Si vous souhaitez obtenir les données out
et err
étaient disponibles lorsque le processus a été interrompu, utilisez la forme alternative POSIX::Spawn::Child.build
pour créer l'enfant sans générer immédiatement le processus. Appelez exec!
pour exécuter la commande à un endroit où vous pouvez détecter toutes les exceptions :
>> child = POSIX::Spawn::Child.build('git', 'log', :max => 100)
>> begin
?> child.exec!
?> rescue POSIX::Spawn::MaximumOutputExceeded
?> # limit was reached
?> end
>> child.out
"commit fa54abe139fd045bf6dc1cc259c0f4c06a9285bbn..."
Veuillez noter que lorsque l'exception MaximumOutputExceeded
est déclenchée, les données réelles combinées out
et err
peuvent être un peu plus longues que la valeur :max
en raison de la mise en mémoire tampon interne.
La méthode POSIX::Spawn::spawn
est conçue pour être aussi compatible que possible avec Process::spawn
de Ruby 1.9. À l’heure actuelle, il s’agit d’un sous-ensemble compatible.
Ces arguments Process::spawn
sont actuellement pris en charge par Spawn::spawn
, Spawn::system
, Spawn::popen4
et Spawn::Child.new
:
env: hash
name => val : set the environment variable
name => nil : unset the environment variable
command...:
commandline : command line string which is passed to a shell
cmdname, arg1, ... : command name and one or more arguments (no shell)
[cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
clearing environment variables:
:unsetenv_others => true : clear environment variables except specified by env
:unsetenv_others => false : don't clear (default)
current directory:
:chdir => str : Not thread-safe when using posix_spawn (see below)
process group:
:pgroup => true or 0 : make a new process group
:pgroup => pgid : join to specified process group
:pgroup => nil : don't change the process group (default)
redirection:
key:
FD : single file descriptor in child process
[FD, FD, ...] : multiple file descriptor in child process
value:
FD : redirect to the file descriptor in parent process
:close : close the file descriptor in child process
string : redirect to file with open(string, "r" or "w")
[string] : redirect to file with open(string, File::RDONLY)
[string, open_mode] : redirect to file with open(string, open_mode, 0644)
[string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
FD is one of follows
:in : the file descriptor 0 which is the standard input
:out : the file descriptor 1 which is the standard output
:err : the file descriptor 2 which is the standard error
integer : the file descriptor of specified the integer
io : the file descriptor specified as io.fileno
Ces options ne sont actuellement PAS prises en charge :
options: hash
resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
:rlimit_resourcename => limit
:rlimit_resourcename => [cur_limit, max_limit]
umask:
:umask => int
redirection:
value:
[:child, FD] : redirect to the redirected file descriptor
file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
:close_others => false : inherit fds (default for system and exec)
:close_others => true : don't inherit (default for spawn and IO.popen)
L'option :chdir
fournie par Posix::Spawn::Child, Posix::Spawn#spawn, Posix::Spawn#system et Posix::Spawn#popen4 n'est pas thread-safe car les processus générés avec l'appel système posix_spawn(2) hérite du répertoire de travail du processus appelant. La gemme posix-spawn contourne cette limitation dans l'appel système en modifiant le répertoire de travail du processus appelant immédiatement avant et après la génération du processus enfant.
Copyright (c) par Ryan Tomayko et Aman Gupta.
Voir le fichier COPYING
pour plus d'informations sur la licence et la redistribution.