wake-up-neo.net

Ist es möglich, ein Programm aus einem Programm heraus neu zu starten?

Ich entwickle ein C++ - Programm und es wäre nützlich, eine Funktion, ein Skript oder etwas zu verwenden, das den Neustart des Programms bewirkt. Es ist ein großes Programm, daher dauert es lange, bis ich alle Variablen manuell neu gestartet habe ...

Ich weiß nicht, ob es einen Weg gibt, dies zu erreichen, oder ob es möglich ist.

47
Julen Uranga

Wenn Sie wirklich das gesamte Programm neu starten müssen (d. H. "Schließen" und "wieder öffnen"), ist es "richtig", ein separates Programm zu haben, mit dem einzigen Zweck, Ihr Hauptprogramm neu zu starten. AFAIK viele Anwendungen mit Auto-Update-Funktion arbeiten auf diese Weise. Wenn Sie also Ihr Hauptprogramm neu starten müssen, rufen Sie einfach das "Neustart" -Programm auf und beenden es.

61
SingerOfTheFall

Sie können eine Schleife in Ihrer main -Funktion verwenden:

int main()
{
    while(!i_want_to_exit_now) {
        // code
    }
}

Wenn Sie das Programm tatsächlich neu starten möchten, führen Sie es von einem Kabelbaum aus:

program "[email protected]"
while [ $? -e 42 ]; do
    program "[email protected]"
done

woher 42 ist ein Rückkehrcode mit der Bedeutung "Bitte neu starten".

Dann würde Ihre restart -Funktion im Programm so aussehen:

void restart() {
    std::exit(42);
}
45
krzaq

Auf Unicies oder irgendwo anders haben Sie execve und es funktioniert wie die Manpage spezifiziert , Sie können einfach ...töte mich, weil ich atoi benutze, weil es im Allgemeinen schrecklich ist, mit Ausnahme dieser Art von Fall.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char** argv) {

  (void) argc;

  printf("arg: %s\n", argv[1]);
  int count = atoi(argv[1]);

  if ( getchar() == 'y' ) {

    ++count;

    char buf[20];
    sprintf(buf, "%d", count);

    char* newargv[3];
    newargv[0] = argv[0];
    newargv[1] = buf;
    newargv[2] = NULL;

    execve(argv[0], newargv, NULL);
  }

  return count;
}

Beispiel:

$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n

7 | $

(7 war der Rückkehrcode).

Es rekursiert nicht und führt keine expliziten Schleifen durch. Stattdessen ruft es sich selbst auf und ersetzt seinen eigenen Speicherplatz durch eine neue Version von sich.

Auf diese Weise wird der Stack niemals überlaufen, obwohl alle vorherigen Variablen wie bei jedem erneuten Aufruf neu deklariert werden - der Aufruf von getchar verhindert eine 100% ige CPU-Auslastung.

Im Fall einer sich selbst aktualisierenden Binärdatei wird, da die gesamte Binärdatei (zumindest unter Unix, ich weiß nicht, wie Windows ist) zur Laufzeit in den Speicher kopiert, wenn sich die Datei vor dem Aufruf von execve(argv[0], ... auf der Festplatte ändert wird stattdessen die neue Binärdatei ausgeführt, die sich auf der Festplatte befindet und nicht dieselbe wie die alte.

Wie @CarstenS und @bishop in den Kommentaren hervorheben, werden offene Dateideskriptoren aufgrund der einzigartigen Art und Weise, in der Unix entworfen wurde, in fork/exec und infolgedessen in folgender Reihenfolge beibehalten Vermeiden Sie es, offene Dateideskriptoren bei Aufrufen von execve zu verlieren. Sie sollten sie entweder vor execve schließen oder sie erst mit e, FD_CLOEXEC/O_CLOEXEC öffnen zu finden auf Dan Walshs Blog .

15
cat

Dies ist eine sehr betriebssystemspezifische Frage. In Windows können Sie Application Restart API oder MFC Restart Manager verwenden. Unter Linux können Sie eine exec() ausführen.

Meistens gibt es jedoch eine bessere Lösung. Wie in anderen Antworten vorgeschlagen, ist es wahrscheinlich besser, eine Schleife zu verwenden.

Dies klingt nach einem falschen Ansatz, da Ihr gesamter Status global ist. Die einzige eindeutige Methode, mit der Sie alles zurücksetzen können (abgesehen davon, dass Sie jeder Variablen manuell "Standard" -Werte zuweisen), besteht darin, das gesamte Programm neu zu starten.

Stattdessen sollte Ihr Status in Objekten (vom Klassentyp oder was auch immer) enthalten sein. Sie können diese Objekte dann jederzeit erstellen und zerstören. Jedes neue Objekt hat einen neuen Status mit "Standard" -Werten.

Kämpfe nicht gegen C++; benutze es!

Sie benötigen wahrscheinlich eine Schleife:

int main()
{
    while (true)
    {
        //.... Program....
    }
}

Bei jedem Neustart rufen Sie continue; innerhalb der Schleife, und um Ihr Programm zu beenden, verwenden Sie break;.

6
Arnav Borborah

Wenn ich Echtzeitsysteme entwickle, ist mein Ansatz normalerweise ein "deriviertes main ()", bei dem ich den gesamten Code schreibe, der von einem echten main () aufgerufen wird, so etwas wie:

Das Programm main.cpp:

int main (int argc, char *argv[])
{
   while (true)
   {
       if (programMain(argc, argv) == 1)
           break;
   }
}

Die programmain.cpp, in der der gesamte Code geschrieben ist:

int programMain(int argc, char *argv[])
{
    // Do whatever - the main logic goes here

    // When you need to restart the program, call
    return 0;

    // When you need to exit the program, call
    return 1;
}

Auf diese Weise wird jedes Mal, wenn wir das Programm beenden, das Programm neu gestartet.

Detail: Alle Variablen, Globale und Logik müssen in programMain()- nichts in "main()" geschrieben werden, mit Ausnahme der Neustart-Steuerung.

Dieser Ansatz funktioniert sowohl auf Linux- als auch auf Windows-Systemen.

4
Mendes

Es klingt für mich so, als würden Sie die falsche Frage stellen, weil Sie nicht genug über das Codieren wissen, um die richtige Frage zu stellen.

Anscheinend möchten Sie einen Code schreiben, der bei einem unbeantworteten Anruf wieder in den Ausgangszustand zurückkehrt und die gesamte Anruf-/Standortsequenz neu startet. In diesem Fall müssen Sie eine Zustandsmaschine verwenden. Schauen Sie nach, was das ist und wie man eins schreibt. Dies ist ein wichtiges Softwarekonzept, und Sie sollten es wissen, wenn Ihre Lehrer in ihrem Beruf gut waren.

Nebenbei bemerkt, wenn Ihr Programm 5 Sekunden benötigt, um alle Ihre Variablen zu initialisieren, dauert es beim Neustart immer noch 5 Sekunden. Das kann man nicht verkürzen. Daher sollte klar sein, dass Sie nicht tun Ihr Programm tatsächlich töten und neu starten möchten, denn dann Sie erhalten genau das Verhalten, das Sie nicht wollen. Mit einer Zustandsmaschine könnten Sie einen Initialisierungszustand für einen Kaltstart haben, in dem das System gerade erst eingeschaltet wurde, und einen zweiten Initialisierungszustand für einen Warmstart.

Oh, und 6 Threads sind nicht sehr viele! :)

2
Graham

Je nachdem, was Sie unter "Neustart" des Programms verstehen, sehe ich einige einfache Lösungen.

Eine besteht darin, das gesamte Programm in eine "Program" -Klasse einzubetten, die im Wesentlichen eine Schleife mit dem richtigen Programm bereitstellt. Wenn Sie das Programm neu starten müssen, rufen Sie die statische öffentliche Methode "Restart" auf, mit der die Schleife erneut gestartet wird.

Sie können auch versuchen, einen systemspezifischen Aufruf durchzuführen, der Ihr Programm erneut startet, und das Programm beenden. Wie in einer anderen Antwort vorgeschlagen, können Sie zu diesem alleinigen Zweck ein Wrapper-Programm erstellen (und den Rückkehrcode überprüfen, um festzustellen, ob Sie das Programm beenden oder neu starten müssen).

Die andere einfache Option ist die Verwendung von goto. Ich weiß, dass die Leute es hassen werden, wenn ich es überhaupt erwähne, aber seien wir ehrlich: Wir wollen ein einfaches Programm erstellen und keine schöne Heizplatte verwenden. Zurückgehen garantiert Zerstörung , so dass Sie ein Programm mit einer Bezeichnung am Anfang und einer Funktion "Neustart" erstellen können, die nur zum Anfang zurückgeht.

Egal für welche Option Sie sich entscheiden, dokumentieren Sie sie gut, damit andere (oder Sie in Zukunft) eine WTF weniger verwenden.

PS. Wie von alain erwähnt, zerstört goto weder globale noch statische Objekte. Gleiches gilt für das Einschließen von Klassen. Daher sollte jeder Ansatz, der nicht das Starten eines neuen Programms anstelle des aktuellen umfasst, entweder die Verwendung globaler/statischer Variablen unterlassen oder geeignete Maßnahmen zum Zurücksetzen dieser Variablen ergreifen (auch wenn dies mühsam sein kann, wie beim Hinzufügen jedes statischen/globalen müssen Sie die Neustartroutine ändern).

1
MatthewRock