Spawne und töte einen Prozess in node.js

70

Ich versuche, einen Prozess in Javascript zu erzeugen und ihn nach einiger Zeit zu beenden (zu Testzwecken).

Am Ende wird der Prozess eine Endlosschleife sein, die ich zu einem bestimmten Zeitpunkt mit verschiedenen Argumenten neu starten muss. Daher dachte ich, dass das Abrufen und Beenden des Prozesses der beste Weg ist, dies zu tun.

Mein Testcode lautet:

var spawn=require('child_process').spawn
, child=null;

child=spawn('omxplayer', ['test.mp4'], function(){console.log('end');}, {timeout:6000});
console.log('Timeout');
setTimeout(function(){
    console.log('kill');
    child.kill();
}, 1200);

child.stdout.on('data', function(data){
    console.log('stdout:'+data);
});

child.stderr.on('data', function(data){
    console.log('stderr:'+data);
});

child.stdin.on('data', function(data){
    console.log('stdin:'+data);
});

Das Ergebnis ist:

#~$ node test.js
Timeout
kill

Aber ich muss immer noch Strg + C senden, um das Programm zu beenden. Was vermisse ich?

Auf Raspbian, Knoten 0.10.17, ist Omxplayer eine Binärdatei (Video-Player).

Ich habe es versucht:

  • chmod +xZur App hinzugefügt .
  • Als root gestartet.
  • Pause des untergeordneten Prozesses. Verwenden aller terminierungsbezogenen Signale im Befehl kill.

Ich habe auch einen psBefehl gestartet, während die App ausgeführt wurde:

2145    bash
2174    node
2175    omxplayer
2176    omxplayer.bin
2177    ps

Omxplayer ist also ein Wrapper, der den untergeordneten Prozess nicht beendet, wenn er endet. Gibt es eine Möglichkeit, die PID des verpackten Prozesses zu ermitteln?

Immer noch beißender Staub, versuchte dies:

spawn('kill', ['-QUIT', '-$(ps opgid= '+child.pid+')']);

Was ich dachte, würde alle Kinder von Omxplayer töten, ich weiß nicht, ob die Verwendung von Spawn so falsch ist oder ob es der Code ist, der nicht funktioniert.

Die letzte Änderung, die ich vorgenommen habe, war die gute Antwort, musste aber etwas bearbeitet werden.

Ich habe eine sh-Datei (mit Ausführungsrecht) wie folgt erstellt:

PID=$1
PGID=$(ps opgid= "$PID")
kill -QUIT -"$PGID"

Was ich so anfange:

execF('kill.sh', [child.pid], function(){
    console.log('killed');
});

Anstelle von child.kill.

Ich bin mir nicht sicher, ob dies der beste Weg ist oder ob der Code sauber ist, aber es funktioniert.

Ich akzeptiere jede Antwort, die es sauberer oder noch besser macht, ohne eine Datei ausführen zu müssen.

DrakaSAN
quelle
Raspberry Pi auf Raspbian (nicht sicher der Version), Knoten 0.10.2. Ich werde versuchen, den Knoten zu aktualisieren.
DrakaSAN
1
Vielleicht ist es etwas mit den Privilegien. Versuchen Sie, Ihr Knotenskript mit Root-Zugriff auszuführen.
Krasimir
Der Knoten wurde auf 0.10.17 aktualisiert und versucht, als Root ausgeführt zu werden. Keiner funktionierte ordnungsgemäß.
DrakaSAN
1
Könnte dies das Problem mit omx sein. Versuchen Sie es zu aktualisieren.
user568109
3
Ich vermute, dass die ausführbare Omxplayer-Datei ein Wrapper für einen Prozess ist, der das Video auf dem Bildschirm ausgibt und den von ihm erzeugten Videoprozess nicht schließt.
user568109

Antworten:

57

Beziehen Sie sich auf diese Diskussion

Sobald Sie anfangen, auf Daten auf stdin zu warten, wartet der Knoten auf die Eingabe auf stdin, bis er angewiesen wird, dies nicht zu tun. Wenn der Benutzer entweder Strg-D (dh Ende der Eingabe) drückt oder das Programm stdin.pause () aufruft, wartet der Knoten nicht mehr auf stdin.

Ein Knotenprogramm wird nur beendet, wenn es nichts zu tun oder zu warten hat. Was passiert ist, es wartet auf stdin und wird daher nie beendet.

Versuchen Sie, Ihren setTimeout-Rückruf in zu ändern

console.log('kill');
child.stdin.pause();
child.kill();

Ich hoffe das sollte funktionieren.

robinkc
quelle
Versuchte dies auch und kommentierte alle "Daten", ich muss noch das Ende des Videos warten.
DrakaSAN
2
Versuchen Sie child.kill ('SIGKILL') und prüfen Sie, ob dies funktioniert. child.kill () sendet SIGHUP, das nur anzeigt, dass das steuernde Terminal geschlossen oder der Steuerungsprozess geschlossen ist. Ich gehe davon aus, dass SIGKILL es mit Sicherheit töten würde.
Robinkc
1
Bei SIGHUP, SIGTERM und SIGKILL wurde versucht, die Nachricht zu töten, aber das Video wird weiterhin abgespielt.
DrakaSAN
Versucht SIGKILL, SIGTERM, SIGABRT, SIGHUP, SIGINT und SIGQUIT, keiner von ihnen hat etwas getan, sogar nacheinander gestartet.
DrakaSAN
Wenn ich versuche, ein Kind eines anderen Befehls wie Pidgin zu erzeugen, funktioniert der Code einfach gut. Aber nicht mit Omxplayer? Hmm.
Robinkc
40

Es gibt ein wirklich nettes npm-Paket, tree-killdas dies sehr einfach und effektiv macht. Es beendet den untergeordneten Prozess und alle untergeordneten Prozesse, die das Kind möglicherweise gestartet hat.

var kill  = require('tree-kill');
const spawn = require('child_process').spawn;

var scriptArgs = ['myScript.sh', 'arg1', 'arg2', 'youGetThePoint'];
var child = spawn('sh', scriptArgs);

// some code to identify when you want to kill the process. Could be
// a button on the client-side??
button.on('someEvent', function(){
    // where the killing happens
    kill(child.pid);
});
Michael Hall
quelle
Vielen Dank. Dies hat bei mir funktioniert und scheint eine großartige Alternative zu sein.
TechnoTim
Das hat bei mir child.kill()funktioniert, aber nicht. Mit NodeJS v14.15.1, Windows 10
Rübe
9

Ich hatte genau das gleiche Problem wie Sie mit Omxplayer und die Lösung in diesem Blog-Beitrag hat für mich funktioniert.

var psTree = require('ps-tree');

var kill = function (pid, signal, callback) {
    signal   = signal || 'SIGKILL';
    callback = callback || function () {};
    var killTree = true;
    if(killTree) {
        psTree(pid, function (err, children) {
            [pid].concat(
                children.map(function (p) {
                    return p.PID;
                })
            ).forEach(function (tpid) {
                try { process.kill(tpid, signal) }
                catch (ex) { }
            });
            callback();
        });
    } else {
        try { process.kill(pid, signal) }
        catch (ex) { }
        callback();
    }
};

// elsewhere in code
kill(child.pid);
Mikenolan
quelle
5

Warum senden Sie nicht einfach den Wert 'q' in die stdin-Pipe? Es beendet den Omxplayer-Prozess.

Superdrac
quelle
kannst du das erklären
Chovy
Es ist das natürliche Verhalten von Omxplayer. Wenn Sie es in der Shell starten, wartet der Prozess auf Verknüpfungen wie "q", die das Programm schließen.
Superdrac
Dies ist eine sehr saubere Lösung und funktioniert auch für andere Programme. f.stdin.write ("q \ n"); weil eine Art Eingabe erforderlich ist.
Evil
1

Sie haben einen untergeordneten Prozess erzeugt, der erfolgreich beendet wurde. Ihr Hauptthread wird jedoch noch ausgeführt, weshalb Sie Strg + C drücken müssen.

Apscience
quelle
2
Wenn es nur der Hauptprozess wäre, der noch läuft, würde ich mich freuen, aber der untergeordnete Prozess läuft offensichtlich noch, da das Video noch abgespielt wird, bis ich Strg + C sende.
DrakaSAN
1

Schließlich fand ich heraus, wie man es ohne Skript macht:

exec('pkill omxplayer', function(err, stdout, stderr){
    if (stdout){console.log('stdout:'+stdout);}
    if (stderr){console.log('stderr:'+stderr);}
    if (err){throw err;}
    //...
}
DrakaSAN
quelle
0

Versuchen Sie, die child_process.execFile()Methode von hier aus zu verwenden .

Die Funktion child_process.execFile () ähnelt child_process.exec (), außer dass keine Shell erzeugt wird. Vielmehr wird die angegebene ausführbare Datei direkt als neuer Prozess erzeugt, wodurch sie etwas effizienter als child_process.exec () ist.

In meinem Fall funktioniert es.

Stinger112
quelle