web-dev-qa-db-fra.com

Bash: sommeil infini (blocage infini)

J'utilise startx pour démarrer X qui évaluera mon .xinitrc. Dans mon .xinitrc Je lance mon gestionnaire de fenêtres avec /usr/bin/mywm. Maintenant, si je tue mon WM (afin de tester un autre WM), X se terminera aussi parce que le .xinitrc le script a atteint EOF. J'ai donc ajouté ceci à la fin de mon .xinitrc:

while true; do sleep 10000; done

De cette façon, X ne se terminera pas si je tue mon WM. Maintenant ma question: comment puis-je faire un sommeil infini au lieu de dormir en boucle? Existe-t-il une commande qui va un peu comme geler le script?

Meilleures salutations

127
watain

sleep infinity fait exactement ce que cela suggère et fonctionne sans abus de chat.

263
Donarsson

Cela semble peut-être moche, mais pourquoi ne pas simplement lancer cat et le laisser attendre pour toujours?

67
Michał Trybus

tail ne bloque pas

Comme toujours: Pour tout, il existe une réponse courte, facile à comprendre, facile à suivre et complètement fausse. Ici tail -f /dev/null Entre dans cette catégorie;)

Si vous le regardez avec strace tail -f /dev/null, Vous remarquerez que cette solution est loin de bloquer! C'est probablement encore pire que la solution sleep de la question, car elle utilise (sous Linux) des ressources précieuses comme le système inotify. De même, d'autres processus qui écrivent dans /dev/null Créent la boucle tail. (Sur mon Ubuntu64 16.10, cela ajoute plusieurs 10 appels système par seconde sur un système déjà occupé.)

La question était pour une commande de blocage

Malheureusement, il n'y a pas une telle chose ..

Lire: Je ne sais pas comment archiver cela directement avec le shell.

Tout (même sleep infinity) Peut être interrompu par un signal. Donc, si vous voulez être vraiment sûr qu'il ne retourne pas exceptionnellement, il doit fonctionner en boucle, comme vous l'avez déjà fait pour votre sleep. Veuillez noter que (sous Linux) /bin/sleep Est apparemment limité à 24 jours (regardez strace sleep infinity), Par conséquent, le mieux que vous puissiez faire est probablement:

while :; do sleep 2073600; done

(Notez que je crois que sleep effectue une boucle interne pour des valeurs supérieures à 24 jours, mais cela signifie: cela ne bloque pas, cela tourne très lentement. Alors pourquoi ne pas déplacer cette boucle vers l'extérieur?)

.. mais vous pouvez venir assez près avec un nom non nommé fifo

Vous pouvez créer quelque chose qui bloque réellement tant qu'aucun signal n'est envoyé au processus. Après utilise bash 4, 2 PID et 1 fifo:

bash -c 'coproc { exec >&-; read; }; eval exec "${COPROC[0]}<&-"; wait'

Vous pouvez vérifier que cela bloque vraiment avec strace si vous aimez:

strace -ff bash -c '..see above..'

Comment cela a été construit

read se bloque s'il n'y a pas de données d'entrée (voir d'autres réponses). Cependant, le tty (alias. stdin) n'est généralement pas une bonne source car il est fermé lorsque l'utilisateur se déconnecte. En outre, il pourrait dérober des informations au tty. Pas gentil.

Pour bloquer read, nous devons attendre quelque chose comme un fifo qui ne retournera jamais rien. Dans bash 4, Il existe une commande qui peut nous fournir exactement un tel fifo: coproc. Si nous attendons aussi le blocage read (qui est notre coproc), nous avons terminé. Malheureusement, deux PID et un fifo doivent rester ouverts.

Variante avec un nom nommé fifo

Si vous ne vous souciez pas d'utiliser un nom nommé fifo, procédez comme suit:

mkfifo "$HOME/.pause.fifo" 2>/dev/null; read <"$HOME/.pause.fifo"

Ne pas utiliser de boucle lors de la lecture est un peu bâclé, mais vous pouvez réutiliser ce fifo aussi souvent que vous le souhaitez et créer la terminaison reads en utilisant touch "$HOME/.pause.fifo" (S'il y en a plus qu’une seule lecture en attente, tous sont terminés en même temps).

Ou utilisez le Linux pause() syscall

Pour le blocage infini, il existe un appel du noyau Linux, appelé pause(), qui fait ce que nous voulons: attendre pour toujours (jusqu'à ce qu'un signal arrive). Cependant, il n'y a pas encore de programme utilisateur.

C

Créer un tel programme est facile. Voici un extrait permettant de créer un très petit programme Linux appelé pause qui s'interrompt indéfiniment (nécessite diet, gcc etc.):

printf '#include <unistd.h>\nint main(){for(;;)pause();}' > pause.c;
diet -Os cc pause.c -o pause;
strip -s pause;
ls -al pause

python

Si vous ne voulez pas compiler quelque chose vous-même, mais que vous avez python installé, vous pouvez l'utiliser sous Linux:

python -c 'while 1: import ctypes; ctypes.CDLL(None).pause()'

(Remarque: utilisez exec python -c ... Pour remplacer le Shell actuel, cela libère un PID. La solution peut être améliorée avec une certaine redirection IO, libérant ainsi les FD inutilisés. toi.)

Comment cela fonctionne (je pense): ctypes.CDLL(None) charge la bibliothèque standard C et exécute la fonction pause() au sein d'une boucle supplémentaire. Moins efficace que la version C, mais fonctionne.

Ma recommandation pour vous:

Reste au sommeil en boucle. Il est facile à comprendre, très portable et bloque la plupart du temps.

57
Tino

TL; DR: sleep infinity Dort réellement le temps maximum autorisé, ce qui est fini.

Me demandant pourquoi cela n’est documenté nulle part, j’ai pris la peine de lire le sources de GNU coreutils et j’ai trouvé qu’il exécutait à peu près ce qui suit:

  1. Utilisez strtod à partir de C stdlib sur le premier argument pour convertir "infinity" en double précision. Donc, en supposant une double précision IEEE 754, la valeur 64 bits l'infini positif est stockée dans la variable seconds.
  2. Invoke xnanosleep(seconds) ( trouvé dans gnulib ), ceci invoque à son tour dtotimespec(seconds) ( également dans gnulib ) pour convertir de double à struct timespec.
  3. struct timespec Est juste une paire de nombres: une partie entière (en secondes) et une partie fractionnaire (en nanosecondes). Une conversion naïve l'infini positif en entier entraînerait un comportement indéfini (voir le §6.3.1.4 du standard C). Par conséquent, il est tronqué à TYPE_MAXIMUM (time_t).
  4. La valeur réelle de TYPE_MAXIMUM (time_t) n'est pas définie dans la norme (même si sizeof(time_t) ne l'est pas); donc, pour l'exemple, prenons x86-64 à partir d'un noyau Linux récent.

Ceci est TIME_T_MAX Dans le noyau Linux, qui est défini ( time.h ) comme:

(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)

Notez que time_t Est __kernel_time_t Et time_t Est long; Le modèle de données LP64 est utilisé, donc sizeof(long) vaut 8 (64 bits).

Ce qui donne: TIME_T_MAX = 9223372036854775807.

C'est-à-dire que sleep infinite Donne un temps de sommeil réel de 9223372036854775807 secondes (10 ^ 11 ans). Et pour les systèmes Linux 32 bits (sizeof(long) est égal à 4 (32 bits)): 2147483647 secondes (68 ans; voir aussi problème de l'année 2038 ).


Edit : apparemment, la fonction nanoseconds appelée n'est pas directement l'appel système, mais un wrapper dépendant du système d'exploitation (également défini dans gnulib ).

Il en résulte une étape supplémentaire: pour certains systèmes où HAVE_BUG_BIG_NANOSLEEP Est true, le sommeil est tronqué à 24 jours, puis appelé en boucle. C'est le cas pour certaines (ou toutes les) distributions Linux. Notez que ce wrapper peut ne pas être utilisé si le test configure - time aboutit ( source ).

En particulier, ce serait 24 * 24 * 60 * 60 = 2073600 seconds (Plus 999999999 nanosecondes); mais ceci est appelé dans une boucle afin de respecter le temps total de sommeil spécifié. Par conséquent, les conclusions précédentes restent valables.


En conclusion, le temps de sommeil qui en résulte n’est pas infini, mais suffisamment élevé pour toutes les utilisations pratiques , même si le laps de temps réellement généré n’est pas portable; cela dépend du système d'exploitation et de l'architecture.

Pour répondre à la question initiale, cela suffit évidemment, mais si pour une raison quelconque (un très système contraint par des ressources), vous voulez vraiment éviter un compte à rebours inutile, je suppose que la solution la plus correcte consiste à utiliser la méthode cat décrite dans d'autres réponses.

21
jp48

sleep infinity semble plus élégant, mais parfois cela ne fonctionne pas pour une raison quelconque. Dans ce cas, vous pouvez essayer d'autres commandes de blocage telles que cat, read, tail -f /dev/null, grep a etc.

8
Hui Zheng

Qu'en est-il de l'envoi d'un SIGSTOP à lui-même?

Cela devrait suspendre le processus jusqu'à la réception de SIGCONT. Ce qui est dans votre cas: jamais.

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
5
michuelnik

J'ai eu récemment besoin de le faire. Je suis arrivé avec la fonction suivante qui permettra à bash de dormir pour toujours sans appeler aucun programme externe:

snore()
{
    local IFS
    [[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null ||
    {
        # workaround for MacOS and similar systems
        local fifo
        fifo=$(mktemp -u)
        mkfifo -m 700 "$fifo"
        exec {_snore_fd}<>"$fifo"
        rm "$fifo"
    }
    read ${1:+-t "$1"} -u $_snore_fd || :
}

REMARQUE: j’avais précédemment posté une version qui ouvrait et fermait le descripteur de fichier à chaque fois, mais j’ai constaté que, sur certains systèmes, le faire des centaines de fois par seconde finissait par se bloquer. Ainsi, la nouvelle solution conserve le descripteur de fichier entre les appels à la fonction. Bash le nettoiera de toute façon à la sortie.

Cela peut être appelé comme/bin/sleep, et il dormira pendant le temps demandé. Appelé sans paramètres, il sera bloqué pour toujours.

snore 0.1  # sleeps for 0.1 seconds
snore 10   # sleeps for 10 seconds
snore      # sleeps forever

Il y a un article avec des détails excessifs sur mon blog ici

2
bolt

Cette approche ne consomme aucune ressource pour maintenir le processus en vie.

while :; do sleep 1; done & kill -STOP $! && wait $!

Panne

  • while :; do sleep 1; done & Crée un processus factice en arrière-plan
  • kill -STOP $! Arrête le processus d'arrière-plan
  • wait $! Attendez le processus en arrière-plan, il bloquera pour toujours, car le processus en arrière-plan a été arrêté avant
0
qoomon

Au lieu de tuer le gestionnaire de fenêtres, essayez d’exécuter le nouveau avec --replace ou -replace si disponible.

0