Bash: sono infinito (bloqueio infinito)

158

Eu uso startxpara iniciar o X, que irá avaliar o meu .xinitrc. No meu .xinitrcinicio meu gerenciador de janelas usando /usr/bin/mywm. Agora, se eu matar o meu WM (para testar outro WM), o X também será encerrado porque o .xinitrcscript alcançou o EOF. Então eu adicionei isso no final do meu .xinitrc:

while true; do sleep 10000; done

Dessa forma, o X não terminará se eu matar o meu WM. Agora minha pergunta: como posso fazer um sono infinito em vez de repetir o sono? Existe um comando que irá congelar o script?

Cumprimentos

watain
fonte

Respostas:

330

sleep infinity faz exatamente o que sugere e funciona sem abuso de gato.

Donarsson
fonte
16
Legal. Infelizmente meu busybox não entende.
não-usuário
12
O BSD (ou pelo menos o OS X) também não entende sleep infinity, embora tenha sido interessante aprender sobre o Linux. No entanto, while true; do sleep 86400; donedeve ser um substituto adequado.
Ivan X
16
Com relação a isso, fiz algumas pesquisas que documentei em uma resposta separada. Para resumir: infinityé convertido em C de "string" para a double. Isso doubleé truncado para os valores máximos permitidos timespec, o que significa uma quantidade muito grande de segundos (dependente da arquitetura), mas, em teoria, finita.
Jp48
72

tail não bloqueia

Como sempre: para tudo, existe uma resposta curta, fácil de entender, fácil de seguir e completamente errada. Aqui se tail -f /dev/nullenquadra nesta categoria;)

Se você olhar com strace tail -f /dev/nullatenção, notará que esta solução está longe de ser bloqueada! Provavelmente é ainda pior do que a sleepsolução em questão, pois utiliza (no Linux) recursos preciosos como o inotifysistema. Também outros processos que escrevem para /dev/nullfazer tailloop. (No meu Ubuntu64 16.10, isso adiciona vários 10 syscalls por segundo em um sistema já ocupado.)

A pergunta era para um comando de bloqueio

Infelizmente, não existe tal coisa ..

Leia: Não conheço nenhuma maneira de arquivar isso diretamente no shell.

Tudo (par sleep infinity) pode ser interrompido por algum sinal. Portanto, se você quiser ter certeza de que ele não retorna excepcionalmente, ele deve ser executado em um loop, como você já fez para o seu sleep. Observe que (no Linux) /bin/sleepaparentemente tem limite de 24 dias (veja strace sleep infinity), portanto, o melhor que você pode fazer provavelmente é:

while :; do sleep 2073600; done

(Observe que acredito em sleeploops internamente para valores mais altos que 24 dias, mas isso significa: não está bloqueando, está fazendo um loop muito lento. Então, por que não mover esse loop para fora?)

.. mas você pode chegar bem perto com um nome fifo

Você pode criar algo que realmente bloqueie, desde que não haja sinais enviados ao processo. Seguintes usos bash 4, 2 PIDs e 1 fifo:

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

Você pode verificar se isso realmente bloqueia stracese você gosta:

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

Como isso foi construído

readbloqueia se não houver dados de entrada (consulte outras respostas). No entanto, o tty(aka. stdin) Geralmente não é uma boa fonte, pois é fechado quando o usuário efetua logout. Também pode roubar alguma entrada do tty. Não é legal.

Para fazer o readbloco, precisamos esperar por algo como um fifoque nunca retorne nada. Em bash 4há um comando que pode exatamente nos fornecer tal um fifo: coproc. Se também esperarmos o bloqueio read(que é o nosso coproc), estamos prontos. Infelizmente, isso precisa manter abertos dois PIDs e um fifo.

Variante com um nome fifo

Se você não se incomodar em usar um nome fifo, faça o seguinte:

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

Não usar um loop na leitura é um pouco desleixado, mas você pode reutilizá-lo fifoquantas vezes quiser e readusar o terminat s touch "$HOME/.pause.fifo"(se houver mais de uma única leitura aguardando, todos serão encerrados de uma só vez).

Ou use o pause()syscall do Linux

Para o bloqueio infinito, há uma chamada do kernel do Linux, chamada pause(), que faz o que queremos: Espere para sempre (até que um sinal chegue). No entanto, ainda não existe um programa de espaço para usuário.

C

Criar esse programa é fácil. Aqui está um trecho para criar um programa de Linux muito pequena chamada pauseque faz uma pausa por tempo indeterminado (necessidades 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

Se você não deseja compilar algo, mas já pythoninstalou, você pode usá-lo no Linux:

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

(Nota: Use exec python -c ...para substituir o shell atual, isso libera um PID. A solução também pode ser aprimorada com algum redirecionamento de E / S, liberando FDs não utilizados. Isso depende de você.)

Como isso funciona (eu acho): ctypes.CDLL(None)carrega a biblioteca C padrão e executa a pause()função nela dentro de algum loop adicional. Menos eficiente que a versão C, mas funciona.

Minha recomendação para você:

Fique no sono em loop. É fácil de entender, muito portátil e bloqueia a maior parte do tempo.

Tino
fonte
1
@ Andrew Normalmente, você não precisa do trap(que modifica o comportamento do shell para sinais) nem do plano de fundo (que permite que o shell intercepte sinais do terminal, como Strg + C). Então sleep infinityé o suficiente (comporta- exec sleep infinityse como se fosse a última afirmação. Para ver a diferença usar strace -ffDI4 bash -c 'YOURCODEHERE'). O sono em loop é melhor, porque sleeppode retornar em determinadas circunstâncias. Por exemplo, você não deseja que o X11 seja desligado repentinamente em um killall sleep, apenas porque .xstartuptermina em sleep infinityvez de um ciclo de suspensão.
Tino
Pode ser um pouco obscuro, mas s6-pauseé um comando da terra do usuário a ser executado pause(), opcionalmente ignorando vários sinais.
Patrick
@Tino /bin/sleepnão é limitado aos 24 dias, como você diz. Seria bom se você pudesse atualizar isso. No Linux agora, esse código está ativo. Limita nanosleep()syscalls individuais a 24 dias, mas os chama em um loop. Portanto sleep infinity, não deve sair após 24 dias. O doubleinfinito positivo é convertido em a struct timespec. Olhando no rpl_nanosleepGDB, infinityé convertido { tv_sec = 9223372036854775807, tv_nsec = 999999999 }no Ubuntu 16.04.
nh2 11/12/17
@ nh2 Já foi mencionado no texto que o sono provavelmente faz um loop em vez de estar totalmente bloqueado. Editei-o agora um pouco para espero tornar esse fato um pouco mais claro. Observe isso " provavelmente ", porque stracesozinho não posso provar o fato de que realmente há algum código de loop compilado sleep, e não quero esperar 24 dias apenas para testar isso (ou descompilar /bin/sleep). É sempre melhor programar defensivamente, se não houver provas matemáticas duras, que algo realmente seja, como parece ser. Também nunca confie em nada:killall -9 sleep
Tino
A opção pause () pode ser feita facilmente com perl: perl -MPOSIX -e 'pause ()'
tgoodhart
70

Talvez isso pareça feio, mas por que não apenas correr cate esperar pela entrada para sempre?

Michał Trybus
fonte
4
Isso não funciona se você não tiver um cachimbo para ler. Por favor informar.
Matt Joiner
2
@ Matt, talvez faça um cachimbo e catisso? mkfifo pipe && cat pipe
Michał Trybus 3/11/11
O que @twalberg diz, mas além disso, você pode imediatamente Reassign a 3 e desvinculá-lo, como mostrado aqui: superuser.com/a/633185/762481
jp48
32

TL; DR: sleep infinitydorme realmente o tempo máximo permitido, que é finito.

Pensando em por que isso não está documentado em nenhum lugar, eu me preocupei em ler as fontes do GNU coreutils e descobri que ele executa aproximadamente o que segue:

  1. Use strtodde C stdlib no primeiro argumento para converter 'infinito' na precisão dupla. Portanto, assumindo a precisão dupla da IEEE 754, o valor do infinito positivo de 64 bits é armazenado na secondsvariável
  2. Invocar xnanosleep(seconds)( encontrado no gnulib ), por sua vez, invoca dtotimespec(seconds)( também no gnulib ) para converter de doublepara struct timespec.
  3. struct timespecé apenas um par de números: parte inteira (em segundos) e parte fracionária (em nanossegundos). A conversão ingenuamente positiva de infinito positivo para inteiro resultaria em comportamento indefinido (consulte §6.3.1.4 do padrão C); portanto, ele truncará para TYPE_MAXIMUM (time_t).
  4. O valor real de TYPE_MAXIMUM (time_t)não está definido no padrão (nem sizeof(time_t)está); então, por uma questão de exemplo, vamos escolher x86-64 de um kernel Linux recente.

Isso está TIME_T_MAXno kernel do Linux, que é definido ( time.h) como:

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

Note que time_té __kernel_time_te time_té long; o modelo de dados LP64 é usado, assim sizeof(long)como 8 (64 bits).

O que resulta em: TIME_T_MAX = 9223372036854775807.

Ou seja: sleep infiniteresulta em um tempo de sono real de 9223372036854775807 segundos (10 ^ 11 anos). E para sistemas linux de 32 bits ( sizeof(long)é de 4 (32 bits)): 2147483647 segundos (68 anos; consulte também o problema do ano 2038 ).


Edit : aparentemente a nanosecondsfunção chamada não é diretamente o syscall, mas um wrapper dependente do SO (também definido no gnulib ).

Há um passo extra, como resultado: para alguns sistemas onde HAVE_BUG_BIG_NANOSLEEPé trueo sono é truncado para 24 dias e, em seguida, chamado em um loop. Este é o caso de algumas (ou todas?) Distribuições Linux. Observe que esse wrapper pode não ser usado se um teste de tempo de configuração for bem-sucedido ( origem ).

Em particular, isso seria 24 * 24 * 60 * 60 = 2073600 seconds(mais 999999999 nanossegundos); mas isso é chamado em um loop para respeitar o tempo total de suspensão especificado. Portanto, as conclusões anteriores permanecem válidas.


Em conclusão, o tempo de suspensão resultante não é infinito, mas alto o suficiente para todos os fins práticos , mesmo que o lapso de tempo real resultante não seja portátil; isso depende do sistema operacional e da arquitetura.

Para responder à pergunta original, isso é obviamente bom o suficiente, mas se, por algum motivo (um sistema com muitos recursos), você realmente deseja evitar um temporizador adicional inútil, acho que a alternativa mais correta é usar o catmétodo descrito em outras respostas .

jp48
fonte
1
Nos próximos coreutils, sleep infinityagora dormirá para sempre sem fazer loop: lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
Vladimir Panteleev
8

sleep infinityparece mais elegante, mas às vezes não funciona por algum motivo. Nesse caso, você pode tentar outros comandos de bloqueio, como cat, read, tail -f /dev/null, grep aetc.

Hui Zheng
fonte
1
tail -f /dev/nullfoi também uma solução trabalhando para mim em uma plataforma SaaS
schmunk
2
tail -f /dev/nulltambém tem a vantagem de não consumir stdin. Eu usei por esse motivo.
Sudo Bash
Aqueles que consideram esta opção devem ler esta resposta para aprender sobre as ramificações dessa opção.
Shadow
6

Que tal enviar um SIGSTOP para si mesmo?

Isso deve pausar o processo até que o SIGCONT seja recebido. Qual é o seu caso: nunca.

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
michuelnik
fonte
6
Os sinais são assíncronos. Portanto, pode acontecer o seguinte: a) o shell chama kill b) kill diz ao kernel que o shell deve receber o sinal STOP c) kill termina e retorna ao shell d) o shell continua (talvez termine porque o script termina) e) o kernel finalmente encontra tempo para entregar sinal STOP para shell
não-usuário
1
@temple Grande insight, não pensou na natureza assíncrona dos sinais. Obrigado!
Michuelnik
4

Deixe-me explicar por que sleep infinityfunciona, embora não esteja documentado. A resposta de jp48 também é útil.

O mais importante: ao especificar infou infinity(sem distinção entre maiúsculas e minúsculas), você pode dormir por mais tempo a sua implementação permitir (ou seja, o menor valor de HUGE_VALe TYPE_MAXIMUM(time_t)).

Agora vamos nos aprofundar nos detalhes. O código-fonte do sleepcomando pode ser lido em coreutils / src / sleep.c . Essencialmente, a função faz isso:

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

Compreensão xstrtod (argv[i], &p, &s, cl_strtod)

xstrtod()

De acordo com o gnulib / lib / xstrtod.c , a chamada de xstrtod()converte a string argv[i]em um valor de ponto flutuante e a armazena *s, usando uma função de conversão cl_strtod().

cl_strtod()

Como pode ser visto em coreutils / lib / cl-strtod.c , cl_strtod()converte uma string em um valor de ponto flutuante usando strtod().

strtod()

De acordo com man 3 strtod, strtod()converte uma string para um valor do tipo double. A página de manual diz

A forma esperada da (parte inicial da) string é ... ou (iii) um infinito, ou ...

e um infinito é definido como

Um infinito é "INF" ou "INFINITY", desconsiderando o caso.

Embora o documento informe

Se o valor correto causar excesso, mais ou menos HUGE_VAL( HUGE_VALF, HUGE_VALL) serão retornados

, não está claro como um infinito é tratado. Então, vamos ver o código-fonte gnulib / lib / strtod.c . O que queremos ler é

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

Assim, INFe INFINITY(não diferenciam maiúsculas de minúsculas) são considerados HUGE_VAL.

HUGE_VAL família

Vamos usar o N1570 como o padrão C. HUGE_VAL, HUGE_VALFE HUGE_VALLmacros são definidos no §7.12-3

A macro se
    HUGE_VAL
expande para uma expressão constante dupla positiva, não necessariamente representável como float. As macros
    HUGE_VALF
    HUGE_VALL
são respectivamente float e long análogos duplos de HUGE_VAL.

HUGE_VAL, HUGE_VALFE HUGE_VALLpode ser infinitos positivos em uma implementação que suporta infinitos.

e em §7.12.1-5

Se um resultado flutuante transborda e arredondamento padrão está em vigor, em seguida, a função retorna o valor da macro HUGE_VAL, HUGE_VALFou HUGE_VALLde acordo com o tipo de retorno

Compreensão xnanosleep (s)

Agora entendemos toda a essência de xstrtod(). Pelas explicações acima, é claro que xnanosleep(s)vimos primeiro realmente significa xnanosleep(HUGE_VALL).

xnanosleep()

De acordo com o código-fonte gnulib / lib / xnanosleep.c , xnanosleep(s)basicamente faz isso:

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

dtotimespec()

Esta função converte um argumento do tipo doubleem um objeto do tipo struct timespec. Como é muito simples, deixe-me citar o código fonte gnulib / lib / dtotimespec.c . Todos os comentários são adicionados por mim.

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_té definido como um tipo integral (consulte §7.27.1-3), é natural assumirmos que o valor máximo do tipo time_té menor que HUGE_VAL(do tipo double), o que significa que entramos no caso de estouro. (Na verdade, essa suposição não é necessária, pois, em todos os casos, o procedimento é essencialmente o mesmo.)

make_timespec()

A última parede que temos que escalar é make_timespec(). Felizmente, é tão simples que citar o código fonte gnulib / lib / timespec.h é 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
fonte
2

Recentemente, tive uma necessidade de fazer isso. Eu vim com a seguinte função que permitirá que o bash durma para sempre sem chamar nenhum 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: Eu publiquei anteriormente uma versão disso que abriria e fecharia o descritor de arquivos a cada vez, mas descobri que em alguns sistemas fazendo isso centenas de vezes por segundo acabaria travando. Portanto, a nova solução mantém o descritor de arquivo entre as chamadas para a função. Bash irá limpá-lo na saída de qualquer maneira.

Isso pode ser chamado como / bin / sleep e permanecerá no tempo solicitado. Chamado sem parâmetros, ele travará para sempre.

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

Há um artigo com detalhes excessivos no meu blog aqui

parafuso
fonte
1

Essa abordagem não consumirá recursos para manter o processo ativo.

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

Demolir

  • while :; do sleep 1; done & Cria um processo fictício em segundo plano
  • kill -STOP $! Pára o processo em segundo plano
  • wait $! Aguarde o processo em segundo plano, isso estará bloqueando para sempre, pois o processo em segundo plano foi interrompido antes
qoomon
fonte
0

Em vez de matar o gerenciador de janelas, tente executar o novo com --replaceou -replacese disponível.

Pausado até novo aviso.
fonte
1
Se eu usar --replace, sempre recebo um aviso como another window manager is already running. Isso não faz muito sentido para mim.
watain 29/05
-2
while :; do read; done

Não há espera para o processo de sono infantil.

shuaiming
fonte
1
Isso come stdinse isso ainda estiver conectado ao tty. Se você executá-lo com < /dev/nullele ocupado-loops. Pode ser de alguma utilidade em determinadas situações, por isso não voto negativo.
Tino
1
Essa é uma péssima idéia, ela consumirá apenas muita CPU.
Mohammed Noureldin