C++ Pid von Fremdprogramm in int vector speichern?

Sadakazu

Well-Known Member
Moin moin....
Ich weiß ich kann mit meinen Fragen nerven....
Hab jetzt auch schon mehrere Stunden damit verbracht via Google und Stackoverflow ne lösung zu finden...
Einige ansätze waren ja schon gut.. aber haben nie wirklich zum erfolg gefürht...
So was will ich?

Ich hab ein Programm laufen, das mehrere Screen-Sessions startet und selbst auch in einer Screensession läuft (gestartet werden sollte...)
Soweit ja auch ganz ok....
Jetzt kann es aber auch mal passieren, das das Programm abstürtzt oder der Benutzer dummerweise mit strg+c das Programm beendet.
Oder das Programm selber hat nicht ordentlich einen Screen Prozess beim "Herunterfahren" gestoppt...
Wie dem auch sei...
Wenn das Programm neu gestartet wird, möchte ich eine Funktion einbauen, die prüft ob noch alte Prozesse am leben sind....

Ein weiteres Problem an der Sache ist....
Ich weiß nicht wie der Benutzer seine Screen-Sessions nennt... Also muss erstmal eine PID abfrage nach allen Screen-Sessions laufen..... und dann alle pids bist auf getpid() (also die eigene PID) mit einem kill -9 killen...

Die Pids würde ich dazu gerne in einem int Vector speichern und dann eben über alle einträge iterieren...

Mein Konkretes Problem ist jetzt.... Wie bekomme ich jetzt die PID's der entsprechenden Prozesse heraus..
getpid() gibt mir ja nur die eigene Pid zurück... und die will ich ja am leben halten...
mit system("pgrep screen") bekomme ich zwar die Pids alle einmal ausgegeben, kann sie aber nicht einzeln in einen vector einlesen.

Die Möglichkeit das ganze in eine Datei umzuleiten und dann die einzelnen Zeilen einlesen würde ich gern vermeiden.

Gibts noch weitere Möglichkeiten?

[EDIT]
Aktuell hab ich es so:
Code:
#include <iostream>
#include <string>
#include <unistd.h>
#include <sstream>
#include <vector>
#include <fstream>

bool isRunning() {

    std::string command = "pgrep screen > pids.txt";
    //system(command.c_str());
    return 0 == system(command.c_str());
 }

void pidToVector(std::vector<std::string>* p) {

   std::ifstream f ("pids.txt");
   for( std::string line; getline( f, line ); )
   {
    p->push_back(line);
   }

}

int main() {

   std::vector<std::string> pid;

   if (isRunning()){
     std::cout << "Some running processes found. Kill'em'All" << std::endl;
     pidToVector(&pid);

   } else {
     std::cout << "no pids found" << std::endl;
   }

   for(unsigned int i = 0; i < pid.size(); i++) {
     std::cout << pid[i] << std::endl;
     if(stoi(pid[i]) != getpid()) {
       std::string command = "kill -9 " + pid[i] + "&& screen -wipe > /dev/null";
       system(command.c_str());
       std::cout << "Kill ProcessID " << pid[i] << std::endl;
     }
   }
   system("rm -rf  pids.txt");
}
Wie gesagt.. die methode mit erst in eine Datei speicher, dann aus der Datei lesen... dann die pids killen und dann die datei löschen.... find ich bisschen umständlich....
 
Zuletzt bearbeitet:
hm....
also ich habs jetzt so:
Code:
#include <iostream>
#include <string>
#include <unistd.h>
#include <sstream>
#include <vector>
#include <fstream>

bool isRunning() {

   // are screens running? store them in pids.txt
    std::string command = "pgrep screen > pids.txt";
    return 0 == system(command.c_str());
 }

void pidToVector(std::vector<std::string>* p) {

   // read pids.txt
   std::ifstream f ("pids.txt");
   for( std::string line; getline( f, line ); )
   {
     //push back into vector
    p->push_back(line);
   }

}

bool killProcess(std::vector<std::string> pid) {

   // iter for every vector
   for(unsigned int i = 0; i < pid.size(); i++) {

     //std::cout << pid[i] << std::endl; // just for debugging

       int ppid = static_cast<int>(getppid()) - 1; //the real pid -1

       //if pid == myPid?
       // || getpid() can be removed??
       if(stoi(pid[i]) == ppid || stoi(pid[i]) == static_cast<int>(getpid())) {

         //debugging output
         std::cout << "dont kill me man!!!" << std::endl;
         std::cout << "my pid is " << static_cast<int>(getpid()) << " and my parents pid is: " << ppid << std::endl;

         //do nothing just contine the loop
         continue;
       } else {

         // if pid is not myPid it can be killed

         // debugging output
         std::cout << "Kill ProcessID " << pid[i] << std::endl;
         sleep(2);

         // create command to kill pid[i]
         std::string command = "kill -9 " + pid[i] + "&& screen -wipe > /dev/null";
         system(command.c_str());

       }
     }
   return true;
}

int main() {



   std::vector<std::string> pid;

   if (isRunning()){
     std::cout << "Some running processes found. Kill'em'All" << std::endl;
     pidToVector(&pid);
     killProcess(pid);

     // Free Memory
     pid.clear();

   } else {
     std::cout << "no pids found" << std::endl;
   }

   // remove pids.txt
   system("rm -rf  pids.txt");

   int a;
   std::cin >> a;
   return 0;
}

Die frage die sich nur stellt... wie bekomme ich std::string command = "pgrep screen > pids.txt"; dazu nicht in die textdatei zu schreiben, sondern direkt in einen vector?
Bleibt mir da echt nur der weg über /proc ?
 
1) PIDs sind Zahlen, warum speicherst du die in einem String?
2) Nutze fork()+exec(), dann brauchst du diese ganzen externen Tools nicht.

Rob
 
@KobRheTilla sagt es bereits. Externe Prozesse per system() zu verwalten ist eine Krücke, die dich auf Dauer nicht glücklich machen wird. Das Prozessmanagement selbst zu implementieren ist kaum aufwändiger und du profitierst davon, dass das Unix-System dir bereits eine Menge Arbeit abnimmt. Wenn du nichts explizit veränderst, ist eigenes Programm der Eltern-Prozess, die von ihm gestarteten Programme sind Kind-Prozesse. Wird ein Kind beendet, bekommt der Eltern-Prozess ein SIGCHLD Signal. Da der Eltern-Prozess, seine Kinder und Kindeskinder eine Prozessgruppe bilden, werden außerdem extern gesendete Signale automatisch an alle Prozesse zugestellt.

Mal grob skizziert:
Code:
#include <iostream>
#include <string>

#include <signal.h>
#include <unistd.h>
#include <wait.h>

static pid_t spawn(std::string str) {
   pid_t pid = fork();

   if (pid != 0) {
     // Parent process
     return pid;
   } else {
     // Child process
     execl("/bin/echo", "/bin/echo", str.c_str(), (char *)NULL);

     // This is never reached
     exit(0);
   }
}

static void handler(int signal) {
   while (waitpid(-1, NULL, WNOHANG) > 0) {
     // write() can be used in signal handlers
     write(STDOUT_FILENO, "A child process died\n", 21);
   }
}

int main() {
   // Register signal handler
   signal(SIGCHLD, handler);

   // Spawn some processes
   std::cout << "Started process: " << spawn("Banane") << std::endl;
   std::cout << "Started process: " << spawn("Melone") << std::endl;
   std::cout << "Started process: " << spawn("Mango") << std::endl;

   // Utterly crude way to wait for children. :(
   while(sleep(2)) {}

   return 0;
}

Zuerst wird ein Signal Handler handler() registriert, der auf das Signal SIGCHLD reagiert. Wird ein SIGCHLD gesendet, wird handler() aufgerufen, wo alle Kinder mit einem veränderten Prozessstatus abgearbeitet werden. Für jeden veränderten Kindprozess wird einfach eine Nachricht auf stdout geschrieben. Im echten Leben muss man hier noch zwei Dinge beachten:
  • SIGCHILD wird bei jeder Änderung des Prozessstatus gesandt, nicht nur beim Beenden.
  • Im Signalhandler sind nur wenige Dinge sicher benutzbar.
Anschließend werden 3 Prozesse durch Aufrufe von spawn() per fork(), gefolgt von einem execl() geforkt. Hier muss man beachten, dass aus Konvention (nahezu) alle Programme als erstes Argument (argv[0] im Programm) den eigenen Aufrufpfad erwarten. Nach dem fork() gibt der Elternprozess die PID zurück, ich gebe sie lediglich aus, du kannst sie in einem vector<pid_t> speichern.

Am Ende warte ich auf krude Art und Weise auf die Kinder. Wenn ein sleep(2) nicht mehr von einem Signal unterbrochen wird, beendet sich das Programm. Das ist natürlich unsicher, aber ich war zu faul nun noch eine richtige Lösung zu tippen. :(
 
Heurikaaa :D
Danke wird mir mit Sicherheit jetzt weiterhelfen....
Gut in meinem Beispiel wollte ich eigentlich im Programmstart gucken:
Hey, sind noch alte Screen-Sessions vorhanden die eigentlich hätten geschlossen sein müssen? (zb weil das Hauptprogramm mit STRG+C einfach beendet wurde anstatt exit zu tippen)
Oder würden im fall von fork() die Kindprozesse ebenfalls gekillt werden wenn der Benutzer im "Elternprozess" STRG+C drück?
Wär auf jedenfall extrem cool....

1) PIDs sind Zahlen, warum speicherst du die in einem String?
Eigentlich ne gute frage.... irgend einen Grund gabs gegen int......
Bin aber nochmal über meinen code drüber geflogen....
Ist mir selber grad schleierhaft....

Vermutlich weil ich neue gestartete Prozesse als string in einen vector schmeiße, da ich das meiste über die Prozessnamen (Screen Session Namen) ansteuere....

Was mich jetzt allerdings geritten hat das bei dem isRunning() einen string zu nutzen.... keine Ahnung.... Werd ich aber auf int ändern xD
 
Oder würden im fall von fork() die Kindprozesse ebenfalls gekillt werden wenn der Benutzer im "Elternprozess" STRG+C drück?
Wär auf jedenfall extrem cool...

Naja ... die Themen Prozesse und Signale sind schon recht komplex.
Ich meine, Du wirst nicht weiterkommen wenn Du hier eine Frage stellst und auf diese Frage eine Antwort bekommst!
Grundsätzlich liegt es ja an Dir, ob der User mit ctrl-c ein Programm terminieren kann und darf!

Es gibt aber noch viele andere Fragen, auch wenn Du das Signal von CTRL-C abfängst.
Was ist wenn der Elternprozess ewig auf den Kindprozess wartet?
Was ist wenn Zombies entstehen ... und und und

Lies Dich mal ins Thema Prozesskommunikation ein, mach kleine Programme die Du testen und probieren kannst - um ein Gefühl dafür zu bekommen - nimm doch als Basis 'mal @Yamagi s Beispiel und spiele mal etwas damit

( ich selbst kenne nur C - vermute aber, dass das alles in C++ nicht komplett anders ist )
 
ich selbst kenne nur C - vermute aber, dass das alles in C++ nicht komplett anders ist
Vermutlich nicht ;)
Aber ich stecke ja auch immer noch in den Anfängen von c++ ^^
nimm doch als Basis 'mal @Yamagi s Beispiel und spiele mal etwas damit
Hab ich gestern noch gemacht..... und mit nem einfachen echo oder ping ohne Pptionen (zb -c 10) funktioniert das auch....

Aber sobald ich Argumente bzw Optionen an das auszuführende Programm übergeben will ist - leider - Feierabend...
Hab da jetzt auch schon mehrere Ansätze versucht...
Screen lässt sich jedenfalls absolut nicht steuern.

Was ist wenn der Elternprozess ewig auf den Kindprozess wartet?
In der tat ein Problem..... wobei ich in der shutdown/stop Prozedur tatsächlich nochmal die PIDs durchgehe und ggf stumpf mit kill die Prozesse töte...

Was ist wenn Zombies entstehen
Und genau darauf bezog sich meine eigentliche Frage...

Mein Problem:
Jemand startet mein Programm... startet die Unterprogramme und drück dann STRG+C...
Was jetzt?! jetzt hab ich Zombies die ich nicht haben will und da ich relativ statische Namen für die Sessions Verwende (die zwar vom User frei beim erstellen einer "Gruppe" gewählt werden können), könnte ich jetzt nicht mal eben das Programm einfach so neu starten... Außerdem muss ich vom DAU aus gehen der grad zu doof ist top/htop zu bedienen und/oder das er einfach vergisst die Zombies zu töten..

Deswegen wollte ich sobald mein Programm (erneut) gestartet wird, als aller erstes, bevor es überhaupt irgend etwas anderes macht, prüft...
Hey gibts noch alte Zombies die eigentlich nicht da sein dürften? wenn ja -> kill... wenn nein... lauf einfach weiter....

Lies Dich mal ins Thema Prozesskommunikation ein,
Doof -.-" Genau das Kapitel scheint in Bjarne Stoustrup's Werk nicht drin vor zu kommen :D
 
Screen lässt sich jedenfalls absolut nicht steuern.
Es ist ja eigentlich nicht meine Art zu sagen, "nimm was anderes, dann geht's". In diesem Fall aber machst du dir das leben mit Screen wirklich schwer. Nimm den tmux, der lässt sich leichter von außen steuern. Ich bin mir allerdings nicht sicher, ob dein Problem Screen ist, oder das Progammieren in C++.

startet die Unterprogramme und drück dann STRG+C...
Das Signal SIGINT kannst du fangen :-)
 
Genau das Kapitel scheint in Bjarne Stoustrup's Werk nicht drin vor zu kommen

Das ist auch kein Bestandteil des C++-Standards sondern von POSIX. Ich empfehle hier "Advanced Programming in the UNIX Environment" von Stevens und Rago. Das ist ein enormer Schinken, aber extrem ausführlich.

( ich selbst kenne nur C - vermute aber, dass das alles in C++ nicht komplett anders ist )

Prozess- und Signalverwaltung werden von POSIX spezifiziert, das sind reine C-Schnittstellen, aber in einem mit C++-kompatiblen Subset.

Jemand startet mein Programm... startet die Unterprogramme und drück dann STRG+C...

Wie @derOliver richtig festgestellt hat wird dadurch das Signal SIGINT generiert. Darauf kannst du mit der richtigen Signal-Behandlung reagieren. Die Funktion die du suchst ist sigaction(2).
 
:D mit SIGINT hab ich grad selber herausgefunden und wollt grad schreiben....
Nice sache bei der ganzen aufregung was gelernt und herausgefunden das ich STRG+C mit SIGINT fangen kann xD
Und natürlich gleich ne Funktion geschrieben die warnt... Bitte nicht mit strg+c das programm beenden! nutze das shutdown command....


Nimm den tmux, der lässt sich leichter von außen steuern.
Puh... nö... :D
mit system() lässt sich auch ne screensession wunderbar steuern.... (wenn man mit Screen umzugehen weiß ;) )
Ich hab ansonsten keine Probleme mit Screen.

Was mein Programm bis jetzt macht....
ich starte es (logischerweise) mit Screen...

$ sceen -S MyProgramm ./myprog

Innerhalb des programms kann der benutzer zb eingeben...
start serverName 1
Dann wird im programm ne funkeion aufgerufen die dann im endeffekt ausführt:
$ sceen -dmS serverName-1 java -jar serverName-1.jar

Außerdem wird der sessionname (also serverName-1) in einem vector abgelegt...
Gibt der benutzer jetzt ein:
stop serverName 1

Wird die stop funktion aufgerufen die im endeffekt folgendes tut...
$ screen -p 0 -S serverName-1 -X stuff 'stop\015'
$ screen -p 0 -S serverName-1 -X stuff 'exit\015'
suche und lösche serverName-1 aus vector

Natürlich besteht die gefahr das wenn serverName-1 innerhalb der screenSession abstürtzt, das auch mein programm das nicht "schnallt"
Dafür hat aber serverName-1 eine autoRestart funktion sollte das programm abstürzen... und da es innerhalb der screensession läuft, startet es auch in dieser screensession neu.... ergo... brauch ich mich nicht wirklich drum zu kümmern.....

Trotzdem mal ne frage....
Ich habs jetzt soweit hinbekommen das wenigstens screen startet (halt komplett ohne irgendwelche argumente...
Warum stirbt mit das kind sofort nach dem aufruf von screen weg?
Der Prozess steht ja weiter?
A child process died
Wird halt direkt ausgerufen...
schaue ich in der konsole rein lebt die session aber noch oO
 
mit system() lässt sich auch ne screensession wunderbar steuern....

Trotzdem mal ne frage....
Ich habs jetzt soweit hinbekommen das wenigstens screen startet (halt komplett ohne irgendwelche argumente...
Warum stirbt mit das kind sofort nach dem aufruf von screen weg?
Der Prozess steht ja weiter?

....oO

Welche Fortschritte macht denn Dein Projekt mit System()?

Versuch doch mal den Ansatz mit fork() - übernimm doch mal die von @Yamagi hier gepostete Struktur und versuche doch mal Deinen Code da zu integrieren!
 
Welche Fortschritte macht denn Dein Projekt mit System()?
An und für sich funktioniert es....
Manchmal frag ich mich zwar warum etwas funktioniert :D aber gut...
Bin mit dem Stroustrup Wälzer noch nicht durch.....

Kindprozesse zu erzeugen wäre schon ne schöne Sache ;) wird aber auch leider im Stroustrup nicht behandelt.
ich bleib da aber am Ball....

Bis auf nen Socket mit Server und Client funktioniert das System lokal schon so wie es funktionieren soll.... ^^
Aktuell bau ich ne sqlite3 DB, um die Daten die ich nur "temporär" im Programm speichere (Welche Gruppen laufen? wieviele Server der Gruppe sind aktiv usw), direkt in eine Datenbank zu schreiben.
Wären sonst zu viele einzelconfigs wenn ich für jede gruppe eine eigene Config Datei schreiben würde....
Außerdem auch übersichtlicher...

Die letzten schritte wäre dann ein Socket, und ein zweites P

rogramm wo die gruppen dann nachher drinne laufen.... ;)
Aber beim Socket weiß ich jetzt schon das ich verkacken werde xD
 
Zurück
Oben