Bash: sueño infinito (bloqueo infinito)

158

Yo uso startxpara iniciar X que evaluará mi .xinitrc. En mi .xinitrcinicio mi administrador de ventanas usando /usr/bin/mywm. Ahora, si elimino mi WM (para probar alguna otra WM), X terminará también porque el .xinitrcscript alcanzó EOF. Así que agregué esto al final de mi .xinitrc:

while true; do sleep 10000; done

De esta manera, X no terminará si elimino mi WM. Ahora mi pregunta: ¿cómo puedo hacer un sueño infinito en lugar de un sueño en bucle? ¿Hay algún comando que parezca congelar el script?

Atentamente

camarero
fuente

Respuestas:

330

sleep infinity hace exactamente lo que sugiere y funciona sin abuso de gatos.

Donarsson
fuente
16
Frio. Lamentablemente mi busybox no entiende.
no es un usuario
12
BSD (o al menos OS X) tampoco entiende sleep infinity, aunque fue algo genial de aprender para Linux. Sin embargo, while true; do sleep 86400; donedebería ser un sustituto adecuado.
Ivan X
16
Con respecto a esto, hice algunas investigaciones que documenté en una respuesta separada. Para resumir: infinityse convierte en C de "cadena" a a double. Entonces eso doublese trunca a los valores máximos permitidos timespec, lo que significa una gran cantidad de segundos (depende de la arquitectura) pero, en teoría, finito.
jp48
72

tail no bloquea

Como siempre: para todo hay una respuesta que es corta, fácil de entender, fácil de seguir y completamente errónea. Aquí tail -f /dev/nullentra en esta categoría;)

Si lo mira con strace tail -f /dev/nullatención, notará que esta solución está lejos de bloquearse. Probablemente sea incluso peor que la sleepsolución en la pregunta, ya que utiliza (bajo Linux) recursos preciosos como el inotifysistema. También otros procesos que escriben para /dev/nullhacer un tailbucle. (En mi Ubuntu64 16.10 esto agrega varias 10 llamadas al sistema por segundo en un sistema que ya está ocupado).

La pregunta era para un comando de bloqueo

Lamentablemente, no existe tal cosa ...

Leer: No conozco ninguna forma de archivar esto con el shell directamente.

Todo (incluso sleep infinity) puede ser interrumpido por alguna señal. Entonces, si desea estar realmente seguro de que no regresa excepcionalmente, debe ejecutarse en un bucle, como ya lo hizo para su sleep. Tenga en cuenta que (en Linux) /bin/sleepaparentemente tiene un límite de 24 días (eche un vistazo strace sleep infinity), por lo tanto, lo mejor que puede hacer es:

while :; do sleep 2073600; done

(Tenga en cuenta que creo que los sleepbucles internos para valores superiores a 24 días, pero esto significa: no está bloqueando, está haciendo un bucle muy lento. Entonces, ¿por qué no mover este bucle al exterior?)

.. pero puedes acercarte bastante con un nombre sin nombre fifo

Puede crear algo que realmente bloquee siempre que no se envíen señales al proceso. Siguientes usos bash 4, 2 PID y 1 fifo:

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

Puedes comprobar que esto realmente bloquea stracesi quieres:

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

Cómo se construyó esto

readbloquea si no hay datos de entrada (ver algunas otras respuestas). Sin embargo, el tty(alias stdin) generalmente no es una buena fuente, ya que se cierra cuando el usuario cierra sesión. También podría robar alguna información del tty. No está bien.

Para hacer un readbloqueo, debemos esperar algo como algo fifoque nunca devolverá nada. En bash 4existe un comando que exactamente nos puede dar un ejemplo fifo: coproc. Si también esperamos el bloqueo read(que es nuestro coproc), hemos terminado. Lamentablemente, esto debe mantener abiertos dos PID y a fifo.

Variante con un nombre fifo

Si no te molestas en usar un nombre fifo, puedes hacerlo de la siguiente manera:

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

No usar un bucle en la lectura es un poco descuidado, pero puede reutilizarlo tantas fifoveces como lo desee y hacer que el reads termine usando touch "$HOME/.pause.fifo"(si hay más de una sola lectura en espera, todas se terminan a la vez).

O use el pause()syscall de Linux

Para el bloqueo infinito hay una llamada al kernel de Linux, llamada pause(), que hace lo que queremos: esperar para siempre (hasta que llegue una señal). Sin embargo, no hay un programa de espacio de usuario para esto (todavía).

C

Crear un programa así es fácil. Aquí hay un fragmento para crear un programa Linux muy pequeño llamado pauseque se detiene indefinidamente (necesidades diet, gccetc.):

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 no desea compilar algo usted mismo, pero lo ha pythoninstalado, puede usar esto en Linux:

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

(Nota: se usa exec python -c ...para reemplazar el shell actual, esto libera un PID. La solución se puede mejorar también con alguna redirección de E / S, liberando FD no utilizados. Esto depende de usted).

Cómo funciona esto (creo): ctypes.CDLL(None)carga la biblioteca C estándar y ejecuta la pause()función dentro de un bucle adicional. Menos eficiente que la versión C, pero funciona.

Mi recomendación para ti:

Quédate en el sueño en bucle. Es fácil de entender, muy portátil y bloquea la mayor parte del tiempo.

Tino
fuente
1
@ Andrew Normalmente no necesita el trap(que modifica el comportamiento del shell a las señales) ni el fondo (que permite que el shell intercepte las señales del terminal, como Strg + C). Entonces sleep infinityes suficiente (se comporta como exec sleep infinitysi fuera la última declaración. Para ver la diferencia de uso strace -ffDI4 bash -c 'YOURCODEHERE'). El sueño en bucle es mejor, porque sleeppuede regresar en ciertas circunstancias. Por ejemplo, no desea que X11 se apague repentinamente en un killall sleep, simplemente porque .xstartuptermina en sleep infinitylugar de un ciclo de suspensión.
Tino
Puede ser un poco oscuro, pero s6-pausees un comando userland para ejecutar pause(), ignorando opcionalmente varias señales.
Patrick
@Tino /bin/sleepno tiene límite a los 24 días como usted dice. Sería bueno si pudieras actualizar eso. En Linux en este momento, este código está activo. Limita las nanosleep()llamadas al sistema individuales a 24 días, pero las llama en un bucle. Por sleep infinitylo tanto , no debe salir después de 24 días. El doubleinfinito positivo se convierte en a struct timespec. Mirando rpl_nanosleepen GDB, infinityse convierte { tv_sec = 9223372036854775807, tv_nsec = 999999999 }en Ubuntu 16.04.
nh2
@ nh2 Ya se mencionó en el texto que el sueño probablemente se repite en lugar de bloquearse por completo. Lo edité ahora un poco para que este hecho sea un poco más claro. Tenga en cuenta esto " probablemente ", porque stracesolo no puedo probar el hecho de que realmente hay un código de bucle compilado sleep, y no quiero esperar 24 días solo para probar esto (o descompilar /bin/sleep). Siempre es mejor programar a la defensiva, si no hay una prueba matemática sólida, de que algo realmente es, como parece ser. Además, nunca confíes en nada:killall -9 sleep
Tino
La opción pause () se puede hacer fácilmente con perl: perl -MPOSIX -e 'pause ()'
tgoodhart
70

Tal vez esto parezca feo, pero ¿por qué no simplemente correr caty dejar que espere la entrada para siempre?

Michał Trybus
fuente
44
Esto no funciona si no tiene un tubo colgante para leer. Por favor avise.
Matt Joiner
2
@ Matt, tal vez hacer una pipa y cateso? mkfifo pipe && cat pipe
Michał Trybus
Lo @twalberg dice, pero, además, se puede reasignar inmediatamente a 3 y desvincularla, como se muestra aquí: superuser.com/a/633185/762481
jp48
32

TL; DR: en sleep infinityrealidad duerme el tiempo máximo permitido, que es finito.

Preguntándome por qué esto no está documentado en ninguna parte, me molesté en leer las fuentes de los núcleos de GNU y descubrí que ejecuta aproximadamente lo siguiente:

  1. Use strtodC stdlib en el primer argumento para convertir 'infinito' a la precisión doble. Por lo tanto, suponiendo que IEEE 754 doble precisión, el valor de infinito positivo de 64 bits se almacena en la secondsvariable.
  2. Invocar xnanosleep(seconds)(que se encuentra en gnulib ), esto a su vez invoca dtotimespec(seconds)( también en gnulib ) para convertir de doublea struct timespec.
  3. struct timespeces solo un par de números: parte entera (en segundos) y parte fraccionaria (en nanosegundos). La conversión ingenua de infinito positivo a entero daría como resultado un comportamiento indefinido (consulte §6.3.1.4 del estándar C), por lo que en su lugar se trunca TYPE_MAXIMUM (time_t).
  4. El valor real de TYPE_MAXIMUM (time_t)no está establecido en el estándar (incluso sizeof(time_t)no lo está); entonces, por ejemplo, escojamos x86-64 de un kernel de Linux reciente.

Esto está TIME_T_MAXen el kernel de Linux, que se define ( time.h) como:

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

Tenga en cuenta que time_tes __kernel_time_ty time_tes long; se utiliza el modelo de datos LP64, también lo sizeof(long)es 8 (64 bits).

Que se traduce en: TIME_T_MAX = 9223372036854775807.

Es decir: sleep infiniteda como resultado un tiempo de sueño real de 9223372036854775807 segundos (10 ^ 11 años). Y para sistemas Linux de 32 bits ( sizeof(long)es 4 (32 bits)): 2147483647 segundos (68 años; ver también el problema del año 2038 ).


Editar : aparentemente la nanosecondsfunción llamada no es directamente la llamada al sistema, sino un contenedor dependiente del sistema operativo (también definido en gnulib ).

Hay un paso adicional como resultado: para algunos sistemas en los que HAVE_BUG_BIG_NANOSLEEPes trueel sueño se trunca a 24 días y luego se llama en un bucle. Este es el caso de algunas (o todas) distribuciones de Linux. Tenga en cuenta que este contenedor no se puede usar si una prueba de configuración de tiempo tiene éxito ( fuente )

En particular, eso sería 24 * 24 * 60 * 60 = 2073600 seconds(más 999999999 nanosegundos); pero esto se llama en un bucle para respetar el tiempo de sueño total especificado. Por lo tanto, las conclusiones anteriores siguen siendo válidas.


En conclusión, el tiempo de sueño resultante no es infinito, sino lo suficientemente alto para todos los fines prácticos , incluso si el lapso de tiempo real resultante no es portátil; eso depende del sistema operativo y la arquitectura.

Para responder a la pregunta original, esto es obviamente lo suficientemente bueno, pero si por alguna razón (un sistema muy limitado en recursos) realmente desea evitar un temporizador de cuenta regresiva adicional inútil, supongo que la alternativa más correcta es usar el catmétodo descrito en otras respuestas .

jp48
fuente
1
En los próximos coreutils, sleep infinityahora dormirá para siempre sin bucles: lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
Vladimir Panteleev
8

sleep infinityse ve muy elegante, pero a veces no funciona por alguna razón. En ese caso, puede probar otros órdenes de bloqueo tales como cat, read, tail -f /dev/null, grep aetc.

Hui Zheng
fuente
1
tail -f /dev/nulltambién fue una solución de trabajo para mí en una plataforma SaaS
schmunk
2
tail -f /dev/nullTambién tiene la ventaja de no consumir stdin. Lo he usado por esa razón.
Sudo Bash
Aquellos que consideren esta opción deben leer esta respuesta para conocer las ramificaciones de esta opción.
Sombra
6

¿Qué pasa con el envío de un SIGSTOP para sí mismo?

Esto debería pausar el proceso hasta que se reciba SIGCONT. Lo cual es en tu caso: nunca.

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
michuelnik
fuente
66
Las señales son asíncronas. Por lo tanto, puede ocurrir lo siguiente: a) el shell llama a la muerte b) kill le dice al núcleo que el shell recibirá la señal STOP c) kill termina y regresa al shell d) el shell continúa (tal vez termina porque el script termina) e) el kernel finalmente encuentra el tiempo para entregar señal de DETENER a shell
no-un-usuario
1
@temple Gran conocimiento, no pensé en la naturaleza asincrónica de las señales. ¡Gracias!
michuelnik
4

Permítanme explicar por qué sleep infinityfunciona, aunque no está documentado. La respuesta de jp48 también es útil.

Lo más importante: al especificar info infinity(sin distinción entre mayúsculas y minúsculas), puede dormir durante el tiempo más largo que su implementación lo permita (es decir, el valor menor de HUGE_VALy TYPE_MAXIMUM(time_t)).

Ahora profundicemos en los detalles. El código fuente del sleepcomando se puede leer desde coreutils / src / sleep.c . Esencialmente, la función hace esto:

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

Comprensión xstrtod (argv[i], &p, &s, cl_strtod)

xstrtod()

De acuerdo con gnulib / lib / xstrtod.c , la llamada de xstrtod()convierte la cadena argv[i]a un valor de punto flotante y la almacena *s, utilizando una función de conversión cl_strtod().

cl_strtod()

Como se puede ver en coreutils / lib / cl-strtod.c , cl_strtod()convierte una cadena a un valor de coma flotante, utilizando strtod().

strtod()

Según man 3 strtod, strtod()convierte una cadena en un valor de tipo double. La página del manual dice

La forma esperada de la (porción inicial de la cadena) es ... o (iii) un infinito, o ...

y un infinito se define como

Un infinito es "INF" o "INFINITY", sin tener en cuenta el caso.

Aunque el documento dice

Si el valor correcto causaría un desbordamiento, se devuelve más o menos HUGE_VAL( HUGE_VALF, HUGE_VALL)

, no está claro cómo se trata un infinito. Así que veamos el código fuente gnulib / lib / strtod.c . Lo que queremos leer es

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

Por lo tanto, INFy INFINITY(ambos no distinguen entre mayúsculas y minúsculas) se consideran como HUGE_VAL.

HUGE_VAL familia

Vamos a usar N1570 como el estándar C. HUGE_VAL, HUGE_VALFy las HUGE_VALLmacros se definen en §7.12-3

La macro se
    HUGE_VAL
expande a una expresión constante doble positiva, no necesariamente representable como flotante. Las macros
    HUGE_VALF
    HUGE_VALL
son, respectivamente, flotantes y análogos dobles largos de HUGE_VAL.

HUGE_VAL, HUGE_VALFy HUGE_VALLpueden ser infinitos positivos en una implementación que admita infinitos.

y en §7.12.1-5

Si un resultado flotante desborda y redondeo por defecto es en efecto, a continuación, la función devuelve el valor de la macro HUGE_VAL, HUGE_VALFo HUGE_VALLde acuerdo con el tipo de retorno

Comprensión xnanosleep (s)

Ahora entendemos toda la esencia de xstrtod(). De las explicaciones anteriores, es claro como el cristal que xnanosleep(s)hemos visto primero significa realmente xnanosleep(HUGE_VALL).

xnanosleep()

De acuerdo con el código fuente gnulib / lib / xnanosleep.c , xnanosleep(s)esencialmente hace esto:

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

dtotimespec()

Esta función convierte un argumento de tipo doubleen un objeto de tipo struct timespec. Como es muy simple, permítanme citar el código fuente gnulib / lib / dtotimespec.c . Todos los comentarios son agregados por mí.

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

Como time_tse define como un tipo integral (véase §7.27.1-3), es natural que supongamos que el valor máximo de tipo time_tes menor que HUGE_VAL(de tipo double), lo que significa que ingresamos al caso de desbordamiento. (En realidad, esta suposición no es necesaria ya que, en todos los casos, el procedimiento es esencialmente el mismo).

make_timespec()

El último muro que tenemos que trepar es make_timespec(). Afortunadamente, es tan simple que citar el código fuente gnulib / lib / timespec.h es suficiente.

_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;
}
ynn
fuente
2

Recientemente tuve la necesidad de hacer esto. Se me ocurrió la siguiente función que permitirá que bash duerma para siempre sin llamar a ningún programa externo:

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: Publiqué previamente una versión de esto que abriría y cerraría el descriptor de archivo cada vez, pero descubrí que en algunos sistemas que lo hacen cientos de veces por segundo eventualmente se bloqueará. Así, la nueva solución mantiene el descriptor de archivo entre llamadas a la función. Bash lo limpiará a la salida de todos modos.

Esto se puede llamar como / bin / sleep, y se dormirá durante el tiempo solicitado. Llamado sin parámetros, se colgará para siempre.

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

Hay una reseña con detalles excesivos en mi blog aquí.

tornillo
fuente
1

Este enfoque no consumirá ningún recurso para mantener vivo el proceso.

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

Descompostura

  • while :; do sleep 1; done & Crea un proceso ficticio en segundo plano.
  • kill -STOP $! Detiene el proceso en segundo plano.
  • wait $! Espere el proceso en segundo plano, esto se bloqueará para siempre, porque el proceso en segundo plano se detuvo antes
qoomon
fuente
0

En lugar de eliminar el administrador de ventanas, intente ejecutar el nuevo con --replaceo -replacesi está disponible.

Pausado hasta nuevo aviso.
fuente
1
Si uso --replacesiempre recibo una advertencia como another window manager is already running. Eso no tiene mucho sentido para mí.
2010
-2
while :; do read; done

sin esperar el proceso de dormir del niño.

shuaiming
fuente
1
Esto se come stdinsi esto todavía está conectado al tty. Si lo ejecutas con < /dev/nullloops ocupados. Puede ser de alguna utilidad en ciertas situaciones, por lo que no voto negativamente.
Tino
1
Esta es una muy mala idea, solo consumirá una gran cantidad de CPU.
Mohammed Noureldin