Executer du code lorsqu'un programme se termine

Même quand il "crash"

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je suis en train d’écrire un petit programme qui doit contrôler les ventilos de ma carte graphique1. Pour ce faire, je dois d’abord dire au driver de la carte que je veux contrôler les ventilos manuellement. Pour éviter de me retrouver dans une situation où mon programme crash/se ferme après avoir arrêté tous les ventilos2, je voudrais m’assurer de remettre les ventilos en contrôle automatique si le programme se termine.

A priori, c’est quelque chose qui peut se faire directement au sein du programme en utilisant std::atexit (pour les arrêts normaux) et std::signal (dans le cas où le programme est forcé de se terminer). Cependant, il y a certains cas où le programme n’a pas la possibilité de faire tourner de code du tout (par exemple s’il se reçoit un SIGKILL). Est-ce que ce genre d’événement peut arriver dans un autre cas que si l’utilisateur envoi lui-même le SIGKILL?

Une autre solution serait d’exécuter le programme dans un script bash qui devra s’occuper de redonner le contrôle des ventilos si le contrôleur s’arrête. Cependant, ça ne protège pas plus d’un SIGKILL involontaire que la solution précédente.

Est-ce qu’il y a une autre solution qui serait plus simple ou plus robuste?


  1. Un des 3 ventilos est plus bruyant que les autres et je compte le faire tourner uniquement lorsqu’il y en a vraiment besoin.
  2. Étant donné que le driver à le dernier mot, je suppose que ce scénario n’est pas catastrophique et que le driver fera le nécessaire pour éviter une surchauffe.

Une autre solution serait d’exécuter le programme dans un script bash qui devra s’occuper de redonner le contrôle des ventilos si le contrôleur s’arrête. Cependant, ça ne protège pas plus d’un SIGKILL involontaire que la solution précédente.

Est-ce qu’il y a une autre solution qui serait plus simple ou plus robuste?

Berdes

Ça ne protège pas contre un SIGKILL ou un SEGFAULT du processus chargé de lancer le programme et redémarrer les ventilos, mais sinon ça semble une solution acceptable, parce que dans ce cas ton programme sera exécuté dans un processus distinct du script en question : si tu le kill -9 ou qu’il segfaulte, le script qui le lance (le père) reprendra quand même la main.

Sinon tu dois bien avoir moyen de faire ça avec un outil du genre supervisor. Ça me semble inutilement lourd, mais pourquoi pas.

+1 -0

Salut,

Une autre solution, moins propre, est de passer par un cron job. Tu peux faire en sorte que ton programme soit exécuté par cron à intervalles réguliers. La seule chose que tu dois alors gérer en plus est de déterminer si ton programme tourne déjà ou non, ce que tu peux aisément faire, par exemple à l’aide d’un fichier qui contient le pid du programme qui est censé tourner. Tu peux alors vérifier son existence à l’aide de la fonction kill(2) et du « signal » 0.

+3 -0

L’avantage du cronjob c’est que tu t’assures une temps minimal d’indisponibilité des ventilateurs de ta radeon.

+1 -0

@depfryer : le souci pour le PO c’est le signal SIGKILL, parce qu’il n’est pas possible de l’intercepter. Ce signal met fin instantanément au processus cible sans autre forme de procès.

Taurre

J’avais rapidement lu deux-trois infos sur les signaux et il me semblait qu’il était toujours possible pour un programme de les ignorer, non ? J’avais entendu qu’un signal représentait ni plus ni moins un message envoyé au programme.

@depfryer : le souci pour le PO c’est le signal SIGKILL, parce qu’il n’est pas possible de l’intercepter. Ce signal met fin instantanément au processus cible sans autre forme de procès.

Taurre

J’avais rapidement lu deux-trois infos sur les signaux et il me semblait qu’il était toujours possible pour un programme de les ignorer, non ? J’avais entendu qu’un signal représentait ni plus ni moins un message envoyé au programme.

Green

Non un sigkill n’est pas interceptable. C’est le garde-fou ultime pour s’assurer qu’on peut tuer un process sous Linux.

+5 -0

La solution habituelle c’est de gérer ça comme un service systemd, qui pourra le monitorer, et relancer la gestion du driver quand le processus se termine. Ou sinon, le programme userspace doit faire un open sur le device, il suffit de relancer la gestion par le driver au close, qui arrive quelle que soit la raison de terminaison du process. Évidemment, aucune de ces solutions ne protège le matériel contre un process suspendu (SIGSTOP). Peut être que systemd sait aussi réagir quand un process est suspendu.

Ok, donc si je comprends bien, il y a trois problèmes possibles:

  1. le processus s’arrête sans pouvoir agir (SIGKILL).
  2. le processus arrête de fonctionner correctement et s’arrête sans pouvoir nettoyer derrière lui (e.g. mémoire corrompu exactement au mauvais endroit)1
  3. le processus arrête de fonctionner correctement sans s’arrêter (e.g. boucle infini, SIGSTOP).

Utiliser un processus parent pour faire le nettoyage lorsque le fils s’arrête permet de gérer les problèmes 1 et 2 s’ils arrivent au fils, mais pas s’ils arrivent au père. Si le père est plus simple, on peut supposer qu’il y a moins de risque pour le problème 2.

Utiliser un processus externe (via systemd, cron ou autre) pour monitorer le contrôleur devrait permettre de résoudre tous les problèmes en supposant que systemd/cron fonctionne toujours2. D’après la documentation, systemd est probablement la meilleur solution vu qu’il est possible et facile de configurer une action à exécuter en cas d’arrêt (ce qui couvre 1 et 2) et qu’il est aussi possible de configurer un watchdog qui arrêtera le processus s’il est incapable de signaler qu’il fonctionne toujours bien (cas 3).

Merci pour l’aide et les idées!


  1. de manière intéressante, il est possible de réagir à un segfault.
  2. si ce n’est pas le cas, j’ai probablement de plus gros problèmes.
+0 -0

Si ça intéresse quelqu’un, j’ai fini la configuration du service et ça à l’air de fonctionner correctement.

Dans le contrôleur, je lance sd_notify(0, "READY=1") après l’initialisation et sd_notify(0, "WATCHDOG=1") dans la boucle de contrôle (qui doit s’exécuter environ toutes les 500ms).

Voici la configuration du service:

[Unit]
Description=Controls GPU fans
# Le service à le droit de démarrer au maximum 3 fois en 5 minutes.
# S'il y a un problème avec le contrôleur, ça permet d'éviter de le redémarrer en permanance.
StartLimitIntervalSec=5min
StartLimitBurst=3

[Service]
Type=notify
ExecStart=/home/berdes/bin/fanctrl
# On désactive le contrôle automatique des ventilos quand le service s'arrête
ExecStopPost=nvidia-settings -a "[gpu:0]/GPUFanControlState=0"
TimeoutStartSec=10
# Le service doit signaler son bon fonctionnement au moins toutes les 5 secondes.
WatchdogSec=5
Restart=always
RestartSec=5

Vu que je démarre ma session en mode tty et que je lance le serveur X ensuite via startx (et que le contrôleur nécessite le serveur X pour fonctionner), je lance le service dans mon .xinitrc:

# Importe les variables d’environnement nécessaire pour contacter le serveur X
systemctl --user import-environment DISPLAY XAUTHORITY
# Le service fail en boucle si on ferme le serveur X.
# Il faut dire à Systemd que tout va bien pour pouvoir redémarrer le service.
systemctl --user reset-failed fanctrl
systemctl --user start fanctrl
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte