58 votes

Est-il possible de redémarrer un programme depuis l'intérieur d'un programme ?

Je développe un programme C++ et il serait utile d'utiliser une fonction, script ou quelque chose qui fait redémarrer le programme. C'est un gros programme, donc redémarrer toutes les variables manuellement me prendra beaucoup de temps...

Je ne sais pas s'il existe un moyen d'y parvenir ou si c'est possible.

40 votes

Quoi que vous fassiez, n'appelez pas main() dans votre code.

15 votes

"redémarrer toutes les variables manuellement" Wut ?

5 votes

Avez-vous envisagé d'utiliser une boucle ?

67voto

SingerOfTheFall Points 9936

Si vous avez vraiment besoin de redémarrer l'ensemble du programme (c'est-à-dire de le "fermer" et de l'"ouvrir" à nouveau), la "bonne" méthode serait d'avoir un programme séparé dans le seul but de redémarrer votre programme principal. Je crois savoir que beaucoup d'applications avec une fonction de mise à jour automatique fonctionnent de cette façon. Ainsi, lorsque vous devez redémarrer votre programme principal, vous appelez simplement le programme "redémarreur" et vous quittez.

0 votes

Il suffirait donc d'avoir deux programmes qui s'appellent l'un l'autre et se quittent lorsqu'un redémarrage est nécessaire, n'est-ce pas ?

7 votes

Ils ne s'appellent pas l'un l'autre ; l'appelant est en fait une boucle qui appelle le second, attend qu'il se termine, puis l'appelle à nouveau. Le "vrai" programme ne sait rien de l'appelant ; il se contente de sortir.

3 votes

Et le travailleur peut indiquer par son code de sortie s'il veut être relancé par le chien de garde.

55voto

KrzaQ Points 1

Vous pouvez utiliser une boucle dans votre main fonction :

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

Ou, si vous voulez réellement redémarrer le programme, exécutez-le à partir d'un harnais :

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

donde 42 est un code de retour signifiant "redémarrer, s'il vous plaît".

Ensuite, dans le programme, votre restart ressemblerait à ceci :

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

5 votes

La première solution ne fonctionnera pas bien si le programme n'est pas bien écrit. Par exemple, s'il contient une fuite de mémoire, celle-ci ne sera pas libérée en passant simplement par une boucle. Cependant, l'autre solution que vous avez fournie permettra de gérer même ce genre de problèmes.

16 votes

@HumamHelfawi Si le programme a des fuites de mémoire, il faut les corriger de toute façon, qu'il soit exécuté en boucle ou non. Avec la même logique, on pourrait dire que la première approche n'est pas bonne car si le programme a des UB, il aura des UB plusieurs fois...

4 votes

@tobi303 Non, ce n'est pas bon. Cependant, l'expression "redémarrer le programme" signifie quelque chose comme "nettoyer toutes les ordures" ou en d'autres termes : "tout effacer et repartir à zéro".. Donc je suppose que le nettoyage de toutes les fuites en fait partie... Quoi qu'il en soit, je suis d'accord avec vous pour dire que personne ne laisse de fuite et n'utilise la méthode du redémarrage pour la résoudre. Je pensais juste que cela valait la peine d'être mentionné.

17voto

cat Points 1596

Sur Unicies, ou n'importe où ailleurs, vous avez execve et cela fonctionne comme la page de manuel précise vous pouvez juste... me tuer pour avoir utilisé <code>atoi</code> parce que c'est généralement horrible, sauf pour ce genre de cas.

#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;
}

Exemple :

$ ./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 était le code de retour).

Il ne récure ni ne boucle explicitement - au lieu de cela, il s'appelle lui-même, remplaçant son propre espace mémoire par une nouvelle version de lui-même.

De cette manière, la pile ne débordera jamais, bien que toutes les variables précédentes soient redéclarées, comme lors de toute réinvocation -- la fonction getchar empêche une utilisation à 100% du CPU.

Dans le cas d'un binaire auto-actualisé, puisque le binaire entier (du moins, sur les systèmes Unix, je ne sais pas pour Windows) sera copié en mémoire au moment de l'exécution, alors si le fichier change sur le disque avant que la commande execve(argv[0], ... le nouveau binaire trouvé sur le disque, et non le même ancien, sera exécuté à la place.

Comme @CarstenS et @bishop le soulignent dans les commentaires, en raison de la manière unique dont Unix a été conçu, les descripteurs de fichiers ouverts sont conservés dans tous les fichiers de la base de données. fork / exec et, par conséquent, afin d'éviter les fuites de descripteurs de fichiers ouverts entre les appels à la fonction execve vous devez soit les fermer avant execve ou les ouvrir avec e , FD_CLOEXEC / O_CLOEXEC en premier lieu -- plus d'informations peuvent être trouvées sur Le blog de Dan Walsh .

3 votes

Qu'en est-il des ressources qui ont pu être acquises ?

1 votes

@CarstenS Ils ont probablement fait l'objet de fuites. Voir danwalsh.livejournal.com/53603.html

0 votes

@CarstenS Bonne question, je vais devoir faire quelques tests. Mon intuition est que le tas de données disparaît simplement avec le fichier tout le reste de la mémoire du programme et écrasés par un tas "frais", ils sont donc détruits comme lorsqu'un programme se termine.

14voto

Il s'agit d'une question très spécifique au système d'exploitation. Sous Windows, vous pouvez utiliser la fonction API de redémarrage des applications o Gestionnaire de redémarrage MFC . Sous Linux, vous pourriez faire un exec()

Cependant, la plupart du temps, il existe une meilleure solution. Il est probablement préférable d'utiliser une boucle, comme suggéré dans d'autres réponses.

9voto

Cela semble être la mauvaise approche, comme si tout votre état était global et que la seule méthode claire pour tout réinitialiser (autre que d'assigner manuellement des valeurs "par défaut" à chaque variable) était de redémarrer l'ensemble du programme.

Au lieu de cela, votre état doit être conservé dans des objets (de type classe, ou autre). Vous êtes ensuite libre de créer et de détruire ces objets quand vous le souhaitez. Chaque nouvel objet possède un nouvel état avec des valeurs "par défaut".

Ne combattez pas le C++, utilisez-le !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X