Bash: sonno infinito (blocco infinito)


158

Uso startxper avviare X che valuterà il mio .xinitrc. Nel mio .xinitrcavvio il mio gestore di finestre con /usr/bin/mywm. Ora, se uccido il mio WM (per provare un altro WM), anche X terminerà perché lo .xinitrcscript ha raggiunto EOF. Quindi l'ho aggiunto alla fine del mio .xinitrc:

while true; do sleep 10000; done

In questo modo X non terminerà se uccido il mio WM. Ora la mia domanda: come posso fare un sonno infinito invece di dormire in loop? Esiste un comando che potrebbe congelare la sceneggiatura?

I migliori saluti

Risposte:


330

sleep infinity fa esattamente ciò che suggerisce e funziona senza abuso di gatti.


16
Freddo. Purtroppo il mio busybox non capisce.
non utente

12
Anche BSD (o almeno OS X) non capisce sleep infinity, anche se è stata una cosa interessante da imparare per Linux. Tuttavia, while true; do sleep 86400; donedovrebbe essere un sostituto adeguato.
Ivan X

16
A questo proposito, ho fatto alcune ricerche che ho documentato in una risposta separata. Riassumendo: infinityviene convertito in C da "stringa" in a double. Quindi doubleviene troncato ai valori massimi consentiti timespec, il che significa una quantità molto grande di secondi (dipendente dall'architettura) ma, in teoria, finito.
jp48,

72

tail non blocca

Come sempre: per ogni cosa c'è una risposta che è breve, facile da capire, facile da seguire e completamente sbagliata. Qui tail -f /dev/nullrientra in questa categoria;)

Se lo guardi strace tail -f /dev/nullnoterai che questa soluzione è lungi dall'essere bloccata! Probabilmente è anche peggio delsleep soluzione nella domanda, poiché utilizza (sotto Linux) risorse preziose come il inotifysistema. Anche altri processi che scrivono per /dev/nullfaretail loop. (Sul mio Ubuntu64 16.10 questo aggiunge diverse 10 syscall al secondo su un sistema già occupato.)

La domanda era per un comando di blocco

Sfortunatamente, non esiste nulla del genere ..

Leggi: Non conosco alcun modo per archiviare questo direttamente con la shell.

Tutto (anche sleep infinity) può essere interrotto da qualche segnale. Quindi, se vuoi essere davvero sicuro che non ritorni eccezionalmente, deve essere eseguito in un ciclo, come hai già fatto per il tuo sleep. Si noti che (su Linux) /bin/sleepapparentemente è limitato a 24 giorni (dai un'occhiata strace sleep infinity), quindi il meglio che puoi fare probabilmente è:

while :; do sleep 2073600; done

(Nota che credo sleep cicli internamente per valori più alti di 24 giorni, ma questo significa: non si blocca, è in loop molto lentamente. Quindi perché non spostare questo loop verso l'esterno?)

.. ma puoi avvicinarti abbastanza con un senza nome fifo

Puoi creare qualcosa che si blocchi davvero fintanto che non ci sono segnali inviati al processo. A seguito di usi bash 4, 2 PID e 1 fifo:

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

Puoi verificare che questo si blocchi davvero stracese ti piace:

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

Come è stato costruito

readblocca se non ci sono dati di input (vedi alcune altre risposte). Tuttavia, il tty(aka. stdin) Di solito non è una buona fonte, poiché viene chiuso quando l'utente si disconnette. Inoltre potrebbe rubare alcuni input dal tty. Non bello.

Per readbloccare, dobbiamo aspettare qualcosa come un fifoche non restituirà mai nulla. In bash 4c'è un comando che può esattamente fornirci una tale fifo: coproc. Se aspettiamo anche il blocco read(che è il nostro coproc), abbiamo finito. Purtroppo questo deve tenere aperti due PID e afifo .

Variante con un nome fifo

Se non ti preoccupi di usare un nome fifo, puoi farlo come segue:

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

Non usare un ciclo sulla lettura è un po 'sciatto, ma puoi riutilizzarlo tutte fifole volte che vuoi e fare readterminare la s usando touch "$HOME/.pause.fifo"(se ci sono più di una sola lettura in attesa, tutte sono terminate contemporaneamente).

Oppure usa il pause()syscall di Linux

Per il blocco infinito c'è una chiamata al kernel Linux, chiamata pause(), che fa quello che vogliamo: attendere per sempre (fino all'arrivo di un segnale). Tuttavia non esiste un programma di spazio utente per questo (ancora).

C

Creare un programma del genere è facile. Ecco uno snippet per creare un programma Linux molto piccolo chiamato pauseche fa una pausa indefinitamente (esigenze diet, gccecc.):

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

python

Se non vuoi compilare qualcosa da te, ma hai pythoninstallato, puoi usarlo sotto Linux:

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

(Nota: utilizzare exec python -c ... per sostituire la shell corrente, questo libera un PID. La soluzione può essere migliorata anche con un po 'di reindirizzamento IO, liberando FD inutilizzati. Dipende da te.)

Come funziona (penso): ctypes.CDLL(None)carica la libreria C standard ed esegue la pause()funzione in essa all'interno di un ciclo aggiuntivo. Meno efficiente della versione C, ma funziona.

La mia raccomandazione per te:

Resta nel sonno continuo. È facile da capire, molto portatile e si blocca per la maggior parte del tempo.


1
@Andrew Normalmente non è necessario il trap(che modifica il comportamento della shell ai segnali) né lo sfondo (che consente alla shell di intercettare i segnali dal terminale, come Strg + C). Quindi sleep infinityè abbastanza (si comporta come exec sleep infinityse fosse l'ultima affermazione. Per vedere la differenza usare strace -ffDI4 bash -c 'YOURCODEHERE'). Il sonno ciclico è migliore, perché sleeppuò tornare in determinate circostanze. Ad esempio, non si desidera che X11 si spenga improvvisamente su a killall sleep, solo perché .xstartuptermina sleep infinityinvece di un ciclo di sospensione.
Tino,

Può essere un po 'oscuro, ma s6-pauseè un comando userland da eseguire pause(), facoltativamente ignorando vari segnali.
Patrick,

@Tino /bin/sleepnon è limitato a 24 giorni come dici tu. Sarebbe bello se potessi aggiornarlo. Su Linux in questo momento, questo codice è attivo. nanosleep()Limita i singoli syscall a 24 giorni, ma li chiama in loop. Quindi sleep infinitynon dovrebbe uscire dopo 24 giorni. L' doubleinfinito positivo viene convertito in a struct timespec. Guardando rpl_nanosleepin GDB, infinityviene convertito { tv_sec = 9223372036854775807, tv_nsec = 999999999 }in Ubuntu 16.04.
nh2,

@ nh2 È già stato menzionato nel testo che il sonno probabilmente si snoda invece di essere completamente bloccato. L'ho modificato ora leggermente per rendere questo fatto un po 'più chiaro. Si prega di notare questo " probabilmente ", perché da stracesolo non posso dimostrare il fatto che ci sia davvero un codice di loop compilato sleepe non voglio aspettare 24 giorni solo per testarlo (o decompilare /bin/sleep). È sempre meglio programmare in modo difensivo, se non ci sono prove matematiche concrete, che qualcosa sia realmente, come sembra. Inoltre, non fidarti di nulla:killall -9 sleep
Tino,

L'opzione pause () può essere fatta abbastanza facilmente con perl: perl -MPOSIX -e 'pause ()'
tgoodhart

70

Forse questo sembra brutto, ma perché non correre cate lasciar aspettare l'input per sempre?


4
Questo non funziona se non hai una pipa sospesa da cui leggere. Si prega di avvisare.
Matt Joiner,

2
@Matt, forse fare una pipa e catvero? mkfifo pipe && cat pipe
Michał Trybus,

Cosa dice @twalberg, ma in aggiunta puoi riassegnare immediatamente a 3 e scollegarlo, come mostrato qui: superuser.com/a/633185/762481
jp48

32

TL; DR: in sleep infinityrealtà dorme il tempo massimo consentito, che è finito.

Mi chiedevo perché questo non sia documentato da nessuna parte, mi sono preso la briga di leggere le fonti dai coreutils GNU e ho scoperto che esegue approssimativamente quanto segue:

  1. Utilizzare strtodda C stdlib sul primo argomento per convertire "infinito" nella doppia precisione. Quindi, supponendo IEEE 754 doppia precisione, il valore di infinito positivo a 64 bit viene memorizzato nella secondsvariabile.
  2. Richiama xnanosleep(seconds)( trovato in gnulib ), questo a sua volta invoca dtotimespec(seconds)( anche in gnulib ) per convertire da doublea struct timespec.
  3. struct timespecè solo una coppia di numeri: parte intera (in secondi) e parte frazionaria (in nanosecondi). La conversione ingenua dell'infinito positivo in intero comporterebbe un comportamento indefinito (vedere §6.3.1.4 dallo standard C), quindi invece si tronca in TYPE_MAXIMUM (time_t).
  4. Il valore effettivo di TYPE_MAXIMUM (time_t)non è impostato nello standard (anche sizeof(time_t)non lo è); quindi, per esempio, scegliamo x86-64 da un recente kernel Linux.

Questo è TIME_T_MAXnel kernel di Linux, che è definito ( time.h) come:

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

Nota che time_tè __kernel_time_ted time_tè long; viene utilizzato il modello di dati LP64sizeof(long) è 8 (64 bit).

Che si traduce in: TIME_T_MAX = 9223372036854775807.

Cioè: sleep infiniteprovoca un tempo di sonno effettivo di 9223372036854775807 secondi (10 ^ 11 anni). E per sistemi Linux a 32 bit ( sizeof(long)è 4 (32 bit)): 2147483647 secondi (68 anni; vedi anche problema anno 2038 ).


Modifica : apparentemente la nanosecondsfunzione chiamata non è direttamente il syscall, ma un wrapper dipendente dal sistema operativo ( definito anche in gnulib ).

C'è un passo in più in seguito: per alcuni sistemi in cui HAVE_BUG_BIG_NANOSLEEPè trueil sonno viene troncato a 24 giorni e poi chiamato in un ciclo. Questo è il caso di alcune (o tutte?) Distro Linux. Si noti che questo wrapper non può essere utilizzato se un test di configurazione- time ha esito positivo ( origine ).

In particolare, sarebbe 24 * 24 * 60 * 60 = 2073600 seconds(più 999999999 nanosecondi); ma questo viene chiamato in un ciclo per rispettare il tempo di sospensione totale specificato. Pertanto le precedenti conclusioni rimangono valide.


In conclusione, il tempo di sonno risultante non è infinito ma abbastanza alto per tutti gli scopi pratici , anche se il lasso di tempo effettivo risultante non è portatile; dipende dal sistema operativo e dall'architettura.

Per rispondere alla domanda originale, questo è ovviamente abbastanza buono ma se per qualche motivo (un sistema molto limitato di risorse) vuoi davvero evitare un inutile conto alla rovescia extra, immagino che l'alternativa più corretta sia usare il catmetodo descritto in altre risposte .


1
Nei prossimi coreutils, sleep infinityora dormirà per sempre senza loop: lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
Vladimir Panteleev

8

sleep infinitysembra più elegante, ma a volte non funziona per qualche motivo. In tal caso, è possibile provare altri comandi di blocco, come cat, read, tail -f /dev/null, grep aetc.


1
tail -f /dev/nullera anche una soluzione funzionante per me su una piattaforma SaaS
schmunk

2
tail -f /dev/nullha anche il vantaggio di non consumare lo stdin. L'ho usato per questo motivo.
Sudo Bash,

Coloro che considerano questa opzione dovrebbero leggere questa risposta per conoscere le ramificazioni di questa opzione.
Shadow

6

Che dire di inviare un SIGSTOP a se stesso?

Ciò dovrebbe mettere in pausa il processo fino alla ricezione di SIGCONT. Qual è nel tuo caso: mai.

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

6
I segnali sono asincroni. Quindi può succedere quanto segue: a) shell chiama kill b) kill dice al kernel che la shell deve ricevere il segnale STOP c) kill termina e ritorna alla shell d) shell continua (forse termina perché lo script termina) e) il kernel trova finalmente il tempo di consegnare segnale STOP a shell
non utente

1
@temple Ottima intuizione, non ho pensato alla natura asincrona dei segnali. Grazie!
michuelnik,

4

Lasciami spiegare perché sleep infinityfunziona anche se non è documentato. Anche la risposta di jp48 è utile.

La cosa più importante: specificando info infinity(senza distinzione tra maiuscole e minuscole), è possibile dormire per il tempo più lungo consentito dalla propria implementazione (ovvero il valore più piccolo di HUGE_VALe TYPE_MAXIMUM(time_t)).

Ora scaviamo nei dettagli. Il codice sorgente del sleepcomando può essere letto da coreutils / src / sleep.c . In sostanza, la funzione fa questo:

double s; //seconds
xstrtod (argv[i], &p, &s, cl_strtod); //`p` is not essential (just used for error check).
xnanosleep (s);

Comprensione xstrtod (argv[i], &p, &s, cl_strtod)

xstrtod()

Secondo gnulib / lib / xstrtod.c , la chiamata di xstrtod()converte la stringa argv[i]in un valore in virgola mobile e la memorizza in *s, usando una funzione di conversione cl_strtod().

cl_strtod()

Come si può vedere da coreutils / lib / cl-strtod.c , cl_strtod()converte una stringa in un valore in virgola mobile, usando strtod().

strtod()

Secondo man 3 strtod, strtod()converte una stringa in un valore di tipo double. La manpage dice

La forma prevista della stringa (parte iniziale della) è ... o (iii) un infinito, o ...

e un infinito è definito come

Un infinito è "INF" o "INFINITY", indipendentemente dal caso.

Anche se il documento dice

Se il valore corretto provoca overflow, viene restituito più o meno HUGE_VAL( HUGE_VALF, HUGE_VALL)

, non è chiaro come viene trattato un infinito. Vediamo quindi il codice sorgente gnulib / lib / strtod.c . Quello che vogliamo leggere è

else if (c_tolower (*s) == 'i'
         && c_tolower (s[1]) == 'n'
         && c_tolower (s[2]) == 'f')
  {
    s += 3;
    if (c_tolower (*s) == 'i'
        && c_tolower (s[1]) == 'n'
        && c_tolower (s[2]) == 'i'
        && c_tolower (s[3]) == 't'
        && c_tolower (s[4]) == 'y')
      s += 5;
    num = HUGE_VAL;
    errno = saved_errno;
  }

Pertanto, INFe INFINITY(entrambi senza distinzione tra maiuscole e minuscole) sono considerati come HUGE_VAL.

HUGE_VAL famiglia

Usiamo N1570 come standard C. HUGE_VAL, HUGE_VALFE HUGE_VALLle macro sono definite in §7.12-3

La macro si
    HUGE_VAL
espande in una doppia espressione positiva costante, non necessariamente rappresentabile come float. Le macro
    HUGE_VALF
    HUGE_VALL
sono rispettivamente float e doppi analoghi lunghi di HUGE_VAL.

HUGE_VAL, HUGE_VALFe HUGE_VALLpossono essere infiniti positivi in ​​un'implementazione che supporta gli infiniti.

e in §7.12.1-5

Se un risultato galleggiante trabocca e all'arrotondamento di default è attivo, allora la funzione restituisce il valore della macro HUGE_VAL, HUGE_VALFoppure HUGE_VALLa seconda del tipo di ritorno

Comprensione xnanosleep (s)

Ora capiamo tutta l'essenza di xstrtod(). Dalle spiegazioni di cui sopra, è chiarissimo che xnanosleep(s)abbiamo visto prima significa in realtà xnanosleep(HUGE_VALL).

xnanosleep()

Secondo il codice sorgente gnulib / lib / xnanosleep.c , xnanosleep(s)essenzialmente fa questo:

struct timespec ts_sleep = dtotimespec (s);
nanosleep (&ts_sleep, NULL);

dtotimespec()

Questa funzione converte un argomento di tipo doublein un oggetto di tipo struct timespec. Dato che è molto semplice, vorrei citare il codice sorgente gnulib / lib / dtotimespec.c . Tutti i commenti sono aggiunti da me.

struct timespec
dtotimespec (double sec)
{
  if (! (TYPE_MINIMUM (time_t) < sec)) //underflow case
    return make_timespec (TYPE_MINIMUM (time_t), 0);
  else if (! (sec < 1.0 + TYPE_MAXIMUM (time_t))) //overflow case
    return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1);
  else //normal case (looks complex but does nothing technical)
    {
      time_t s = sec;
      double frac = TIMESPEC_HZ * (sec - s);
      long ns = frac;
      ns += ns < frac;
      s += ns / TIMESPEC_HZ;
      ns %= TIMESPEC_HZ;

      if (ns < 0)
        {
          s--;
          ns += TIMESPEC_HZ;
        }

      return make_timespec (s, ns);
    }
}

Poiché time_tè definito come un tipo integrale (vedere §7.27.1-3), è naturale supponiamo che il valore massimo del tipo time_tsia minore di HUGE_VAL(del tipo double), il che significa che entriamo nel caso di overflow. (In realtà questa ipotesi non è necessaria poiché, in tutti i casi, la procedura è essenzialmente la stessa.)

make_timespec()

L'ultima parete che dobbiamo scalare è make_timespec(). Per fortuna, è così semplice che è sufficiente citare il codice sorgente gnulib / lib / timespec.h .

_GL_TIMESPEC_INLINE struct timespec
make_timespec (time_t s, long int ns)
{
  struct timespec r;
  r.tv_sec = s;
  r.tv_nsec = ns;
  return r;
}

2

Di recente ho avuto bisogno di farlo. Mi è venuta in mente la seguente funzione che permetterà a bash di dormire per sempre senza chiamare alcun programma esterno:

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 || :
}

NOTA: in precedenza avevo pubblicato una versione di questo che apriva e chiudeva ogni volta il descrittore di file, ma ho scoperto che su alcuni sistemi che lo facessero centinaia di volte al secondo alla fine si sarebbe bloccato. Pertanto, la nuova soluzione mantiene il descrittore di file tra le chiamate alla funzione. Bash lo pulirà comunque all'uscita.

Questo può essere chiamato proprio come / bin / sleep e dormirà per il tempo richiesto. Chiamato senza parametri, si bloccherà per sempre.

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

C'è un writeup con dettagli eccessivi sul mio blog qui


1

Questo approccio non consumerà alcuna risorsa per mantenere in vita il processo.

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

Abbattersi

  • while :; do sleep 1; done & Crea un processo fittizio in background
  • kill -STOP $! Interrompe il processo in background
  • wait $! Attendere il processo in background, questo si bloccherà per sempre, perché il processo in background è stato interrotto prima

0

Invece di uccidere il gestore delle finestre, prova a eseguire quello nuovo con --replaceo -replacese disponibile.


1
Se uso --replacericevo sempre un avviso simile another window manager is already running. Per me non ha molto senso.
Watain,

-2
while :; do read; done

nessuna attesa per il processo di sonno del bambino.


1
Questo mangia stdinse questo è ancora collegato al tty. Se lo esegui con < /dev/nulli loop di occupato. Potrebbe essere di qualche utilità in determinate situazioni, quindi non declassare.
Tino,

1
Questa è una pessima idea, consumerà solo molto lotto di CPU.
Mohammed Noureldin il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.