PHP gestarteter prozess lässt sich nicht beenden!
SgtIgrams
- php
Ahoi!
ich habe das problem das ich ein prozess folgendermaßen starte und nichtmehr beendet bekomme!!
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('file', 'out.log', 'a'),
2 => array('file', 'err.log', 'w')
);
$cmd = "sudo python /var/www/test.py &> /dev/null &";
$cwd = '/tmp';
$env = array('some_option' => 'aeiou');
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
zu beachten ist natürlich das ich ihn mit sudo starte.. gestartet wird er auch definitiv da ich damit LEDs ansteuer und eine PID wird auch rausgeworfen mit "proc_get_status"
ich habe echt meiner meinung nach alles durchprobiert:
$array = proc_get_status($process);
print_r($array);
$return_value = proc_close($process);
proc_terminate($process);
$cmd = "sudo kill -9 ".$array['pid'];
exec($cmd);
posix_kill($array['pid'], 15);
EDIT: nur ums gesagt zu haben.. zu testzwecken hab ich auf der maschine den user www-data zu den sudoers hinzugefügt... aber wie gesagt.. gestartet wird der prozess einwandfrei.
grüße
Moin!
ich habe das problem das ich ein prozess folgendermaßen starte und nichtmehr beendet bekomme!!
Prozesse, die von PHP mit proc_open()
gestartet werden, werden in einem Prozess von sh
gewrappt. Die PID, die du abfragst, ist die Shell, nicht der eigentliche Prozess. Deswegen killst du auch nur diese Shell, nicht den Prozess.
Das ganze ist in https://bugs.php.net/bug.php?id=39992 etwas genauer aufgedröselt, und definitiv auch mit PHP 5.6 noch immer ein Problem.
Symfony hat eine Wrapper-Klasse für derartige Prozesse, und damit auch ein Problem. Deswegen gibts auf Github dazu dieses "issue": https://github.com/symfony/symfony/issues/5759
Der "Fix" ist, das Kommando mit vorangestelltem "exec" auszuführen. Das vermeidet anscheinend die Wrapper-Shell, die abfragbare PID ist dann die vom tatsächlichen Prozess, und der lässt sich dann auch auf normalem Weg beenden.
$cmd = "sudo python /var/www/test.py &> /dev/null &";
$cmd = "exec sudo python /var/www/test.py &> /dev/null &";
Ich bin mir unsicher wegen sudo, würde auch "sudo exec" in Erwägung ziehen, wenn nötig. Oder am besten ganz auf sudo verzichten. :)
Grüße Sven
Hallo und guten Abend,
Der "Fix" ist, das Kommando mit vorangestelltem "exec" auszuführen. Das vermeidet anscheinend die Wrapper-Shell, die abfragbare PID ist dann die vom tatsächlichen Prozess, und der lässt sich dann auch auf normalem Weg beenden.
Außerdem kann man dem Prozess selber eine Abbruchbedingung mitgeben.
Innerhalb der zweifellos irgendwo im Prozess vorhandenen Endlosschleife baut man einfach ein (gelegentliches, je nach Aufgabe) Abfragen auf die Existenz eines Runfiles ein. Wenn das nicht mehr da ist, muss der Prozess sich (geordnet) beeneden.
Und ein Tipp aus der Praxis: Doppelt genäht hilft hier doch meistens besser ;-)
Grüße
TS
Hallo
ich habe z.Zt. (fast?) das gleiche Problem: ich möchte über eine "Webseite" ein Programm steuern, das LEDs blinken lässt. Meine Umgebung ist ein Raspbery PI mit nginx als Webserver.
Den Prozess, bei mir ein C-Programm, starte ich mit
echo(shell_exec('nohup /home/pi/lauflicht/lauflicht 2> /dev/null > /dev/null & echo $!'));
So wird an die WWW-Seite bzw. an die Callback-Funktion im HTTP-Request die Prozessid des Programms gesendet.
Meine Versuche, den Prozess mit "kill" zu beeinfluseen, sind gescheitert, daher habe ich mir ein C-Programm geschrieben, das das Signal (per kill aus der C-Bibliothek) an den Prozess sendet:
echo(shell_exec('/home/pi/lauflicht/send_sig 2 '.$_GET['pid']));
Gruß Jürgen
yeah, hab meine alten benutzerdaten wiedergefunden :D
jürgen, da es sich bei mir auch um leds per gpios meines raspberrys handelt wäre es sehr interessant sich mal mit dir auszutauschen.
hast du skype/ts oder sowas in die richtung?
grüße -sgtigram(s)
Hallo,
jürgen, da es sich bei mir auch um leds per gpios meines raspberrys handelt wäre es sehr interessant sich mal mit dir auszutauschen.
hast du skype/ts oder sowas in die richtung?
nee, aber ich kann meine Quelltexte mal hier reinstellen:
<?php
if(isset($_GET['cmd'])) {
if($_GET['cmd']=='init') {
// LED-Steuerprogramm starten und PID ausgeben
echo(shell_exec('nohup /home/pi/lauflicht/lauflicht 2> /dev/null > /dev/null & echo $!'));
}
else if($_GET['cmd']=='quit') {
if(isset($_GET['pid'])) {
// LED-Steuerprogramm über PID beenden
echo(shell_exec('/home/pi/lauflicht/send_sig 2 '.$_GET['pid']));
}
else {
echo 'Paramter pid fehlt';
}
}
else if($_GET['cmd']=='next') {
if(isset($_GET['pid'])) {
// Signal SIGUSR1 (10) an LED-Steuerprogramm senden, um Lichtmuster zu ändern
echo(shell_exec('/home/pi/lauflicht/send_sig 10 '.$_GET['pid']));
}
else {
echo 'Paramter pid fehlt';
}
}
else {
echo 'Falscher Parameter';
}
}
else {
echo 'Kein Parameter';
}
?>
// lauflicht.cpp
// g++ lauflicht.cpp -o lauflicht -lwiringPi
// sudo chown root lauflicht
// sudo chmod 4755 lauflicht
// WiringPi-Api einbinden
#include <wiringPi.h>
// C-Standardbibliotheken einbinden
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <math.h>
// Die vier LEDs
#define LED1 15 // GPIO 14, PIN 8
#define LED2 16 // GPIO 15, PIN 10
#define LED3 1 // GPIO 18, PIN 12
#define LED4 4 // GPIO 23, PIN 16
int leds[4] = {LED1,LED2,LED3,LED4};
// Die Muster
#define NM 6
#define NME 16
int muster[NM][NME] = {
{0b1111, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{0b0001,0b0001,0b0011,0b0011,0b0111,0b0111,0b1111,0b1111,0b1111,0b1111,0b1111,0b1111,0b0000,0b0000, -1, -1},
{0b0001,0b0000,0b0011,0b0000,0b0111,0b0000,0b1111,0b0000,0b1111,0b0000,0b1111,0b0000,0b1111,0b0000, -1, -1},
{0b0001,0b0010,0b0100,0b1000,0b0011,0b0110,0b1100,0b1001,0b0111,0b1110,0b1101,0b1011,0b1111,0b1111,0b0000, -1},
{0b0001,0b0011,0b0111,0b1111,0b1111,0b1110,0b1100,0b1000,0b0000, -1, -1, -1, -1, -1, -1, -1},
{0b1000,0b0100,0b0010,0b0001,0b1001,0b0101,0b0011,0b1011,0b0111,0b1111,0b1111,0b0000, -1, -1, -1, -1}
};
int mnr = 0;
int weiter = 1;
void mnr_up(int sig) {
mnr++;
if(mnr >= NM ) mnr = 0;
}
void mnr_down(int sig) {
mnr--;
if(mnr < 0 ) mnr = NM - 1;
}
void ende(int sig) {
weiter = 0;
}
void alle_leds(int n) {
int i,bit,mask,zuf1,zuf2;
// Muster anlegen
for(i=0,mask=1;i<4;i++,mask*=2) {
bit = n & mask;
digitalWrite(leds[i],bit);
}
// Flackern
for(i=0;i<5;i++) {
zuf1 = (int)(((float)rand()*4.0)/RAND_MAX);
zuf2 = (int)(((float)rand()*10.0)/RAND_MAX);
digitalWrite(leds[zuf1],0);
delay(10+zuf2);
mask = pow(2,zuf1);
bit = n & mask;
digitalWrite(leds[zuf1],bit);
delay(90-zuf2);
}
}
int main( int argc, char **argv ) {
int i;
// Übergabeparameter für Musternummer
if(argc == 2) {
mnr = atoi(argv[1]) ;
if(mnr < 0) mnr = 0;
if(mnr >= NM ) mnr = NM-1;
}
// Starte die WiringPi-Api (wichtig)
if (wiringPiSetup() == -1)
return 1;
// Signalhandler anlegen
signal(SIGUSR1,mnr_up); // 10
signal(SIGUSR2,mnr_down); // 12
signal(SIGINT,ende); // 2
// Schalte alle 4 PINs auf Ausgang
for(i=0;i<4;i++) pinMode(leds[i], OUTPUT);
// Zufalsszahlen initialisieren
srand(time(NULL));
// Musterschleife
while(weiter) {
for(i=0;i<NME;i++) {
if(muster[mnr][i]==-1) break;
alle_leds(muster[mnr][i]);
}
}
// Alles auf 0
alle_leds(0);
// Schalte alle 4 PINs auf Eingang
for(i=0;i<4;i++) pinMode(leds[i], INPUT);
}
// send_sig.cpp
// C-Standardbibliotheken einbinden
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main( int argc, char **argv ) {
int sig,ret;
pid_t pid;
// Übergabeparameter für Musternummer
if(argc == 3) {
sig = atoi(argv[1]) ;
pid = atoi(argv[2]) ;
printf("Sende Signal %d an PID %d\n",sig,pid);
ret = kill(pid,sig);
printf("Rückgabe: %d\n",ret);
}
else {
printf("Usage: send_sig sig pid\n");
}
}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Lauflicht</title>
<style>
</style>
</head>
<body>
<h1>Lauflicht</h1>
<p>
<button type="button" onclick="start()">Start</button>
<button type="button" onclick="next()">Next</button>
<button type="button" onclick="quit()">Quit</button>
</p>
<p>PID: <span id="opid"></span></p>
<script>
var hr = function(url,cbf) {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == 4) {
var status = req.status;
if(status == 200) {
req.onreadystatechange = null;
var res = req.responseText;
if(typeof(cbf)=="function") cbf(res);
}
}
}
req.open('GET', url, true);
req.send(null);
}
var pid=-1;
var opid = document.getElementById("opid");
function start() {
if(pid==-1) hr("lauflicht.php?cmd=init",function(res) {
pid = res;
opid.innerHTML = pid;
});
}
function next() {
if(pid>-1) hr("lauflicht.php?cmd=next&pid="+pid,null);
}
function quit() {
if(pid>-1) hr("lauflicht.php?cmd=quit&pid="+pid,function(res) {
pid = -1
opid.innerHTML = "";
});
}
</script>
</body>
</html>
Wie du siehst, steuere ich die PINS über wiringPi an und programmiere das über C, PHP und im Browser dann HTML und Javascript für das Aufrufen des PHP-Scriptes über HTTP-Requests.
Gruß Jürgen
Moin!
Python kennt seine PID:
import os
pid = str(os.getpid())
Die PID in eine Datei wegschreiben und diese auszulesen um den Prozess zu killen könnte also auch gehen.
Jörg Reinholz