Bash: бесконечный сон (бесконечная блокировка) - PullRequest
121 голосов
/ 29 мая 2010

Я использую startx для запуска X, который оценит мои .xinitrc. В моем .xinitrc я запускаю свой оконный менеджер, используя /usr/bin/mywm. Теперь, если я убью свой WM (например, для проверки другого WM), X тоже прекратит работу, потому что сценарий .xinitrc достиг EOF. Поэтому я добавил это в конце моего .xinitrc:

while true; do sleep 10000; done

Таким образом, X не прекратит работу, если я убью свой WM. Теперь мой вопрос: как я могу сделать бесконечный сон вместо зацикливания сна? Есть ли команда, которая вроде как заморозит скрипт?

С наилучшими пожеланиями

Ответы [ 10 ]

252 голосов
/ 28 февраля 2014

sleep infinity делает именно то, что предлагает и работает без злоупотребления кошкой.

64 голосов
/ 29 мая 2010

Может быть, это кажется уродливым, но почему бы просто не запустить cat и позволить ему ждать ввода вечно?

52 голосов
/ 15 января 2017

tail не блокирует

Как всегда: для всего есть ответ, который короток, прост для понимания, легок для понимания и совершенно неверен. Здесь tail -f /dev/null попадает в эту категорию;)

Если вы посмотрите на него с strace tail -f /dev/null, вы заметите, что это решение далеко не блокирует! Вероятно, это даже хуже, чем решение sleep в этом вопросе, поскольку оно использует (под Linux) драгоценные ресурсы, такие как система inotify. Также другие процессы, которые пишут в /dev/null, делают цикл tail. (На моем Ubuntu64 16.10 это добавляет несколько 10 системных вызовов в секунду в уже занятой системе.)

Вопрос был к команде блокировки

К сожалению, такой вещи нет ..

Читайте: я не знаю, как архивировать это напрямую с помощью оболочки.

Все (даже sleep infinity) может быть прервано каким-либо сигналом. Поэтому, если вы хотите быть действительно уверенным, что он не исключительно возвращается, он должен работать в цикле, как вы уже сделали для вашего sleep. Обратите внимание, что (в Linux) /bin/sleep ограничено 24 днями (взгляните на strace sleep infinity), поэтому лучшее, что вы можете сделать, это:

while :; do sleep 2073600; done

(Обратите внимание, что я считаю, что sleep зацикливается внутри для более высоких значений, чем 24 дня, но это означает: это не блокирование, а очень медленное зацикливание. Так почему бы не переместить этот цикл наружу?)

.. но вы можете подойти совсем неназванным fifo

Вы можете создать что-то, что действительно блокирует, пока нет никаких сигналов, посылаемых процессу. Следующее использование bash 4, 2 PID и 1 fifo:

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

Вы можете проверить, что это действительно блокирует с помощью strace, если хотите:

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

Как это было построено

read блокируется, если нет входных данных (см. Некоторые другие ответы). Однако tty (он же stdin) обычно не является хорошим источником, так как он закрывается, когда пользователь выходит из системы. Также это может украсть некоторые данные из tty. Не красиво.

Чтобы сделать блок read, нам нужно ждать что-то вроде fifo, которое никогда ничего не вернет. В bash 4 есть команда, которая может точно предоставить нам такой fifo: coproc. Если мы также подождем блокировки read (что является нашим coproc), мы закончили. К сожалению, для этого нужно держать открытыми два PID и fifo.

Вариант с именем fifo

Если вы не удосужились использовать именованное fifo, вы можете сделать это следующим образом:

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

Не использовать цикл при чтении немного небрежно, но вы можете использовать это fifo столько раз, сколько захотите, и завершить read s, используя touch "$HOME/.pause.fifo" (если есть более одного ожидания чтения , все прекращаются сразу).

Или используйте системный вызов Linux pause()

Для бесконечной блокировки существует вызов ядра Linux, называемый pause(), который делает то, что мы хотим: ждать вечно (пока не поступит сигнал). Однако для этого пока нет программы для пользователя.

C

Создать такую ​​программу легко. Вот фрагмент кода для создания очень маленькой программы для Linux под названием pause, которая приостанавливается на неопределенный срок (требуется diet, gcc и т. Д.):

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

python

Если вы не хотите что-то компилировать самостоятельно, но у вас установлено python, вы можете использовать это под Linux:

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

(Примечание: используйте exec python -c ... для замены текущей оболочки, это освобождает один PID. Решение может быть улучшено также путем некоторого перенаправления ввода-вывода, освобождая неиспользуемые FD.

Как это работает (я думаю): ctypes.CDLL(None) загружает стандартную библиотеку C и запускает в ней функцию pause() в некотором дополнительном цикле. Менее эффективен, чем версия C, но работает.

Моя рекомендация для вас:

Оставайтесь в зацикленном сне. Это легко понять, очень переносимо и большую часть времени блокирует.

20 голосов
/ 30 июля 2017

TL; DR: sleep infinity фактически спит максимально допустимое время, которое является конечным.

Удивляясь, почему это нигде не задокументировано, я потрудился прочитать исходные тексты из GNU coreutils и обнаружил, что он выполняется примерно следующим образом:

  1. Используйте strtod из C stdlib для первого аргумента, чтобы преобразовать бесконечность в двойную точность. Итак, при условии двойной точности IEEE 754 64-битное положительное значение бесконечности сохраняется в переменной seconds.
  2. Invoke xnanosleep(seconds) ( найдено в gnulib ), это, в свою очередь, вызывает dtotimespec(seconds) ( также в gnulib ) для преобразования из double в struct timespec.
  3. struct timespec - это просто пара чисел: целая часть (в секундах) и дробная часть (в наносекундах). Наивное преобразование положительной бесконечности в целое число приведет к неопределенному поведению (см. §6.3.1.4 из стандарта C), поэтому вместо этого оно усекается до TYPE_MAXIMUM (time_t).
  4. Фактическое значение TYPE_MAXIMUM (time_t) не установлено в стандарте (даже sizeof(time_t) нет); Итак, для примера давайте выберем x86-64 из недавнего ядра Linux.

Это TIME_T_MAX в ядре Linux, которое определяется (time.h) как:

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

Обратите внимание, что time_t равно __kernel_time_t и time_t равно long; используется модель данных LP64, поэтому sizeof(long) равно 8 (64 бита).

Что приводит к: TIME_T_MAX = 9223372036854775807.

То есть: sleep infinite приводит к фактическому времени сна 9223372036854775807 секунд (10 ^ 11 лет). А для 32-разрядных систем Linux (sizeof(long) равно 4 (32 бита)): 2147483647 секунд (68 лет; см. Также год 2038, проблема ).


Редактировать : очевидно, что вызываемая функция nanoseconds является не системным вызовом, а зависимой от ОС оболочкой (также , определенной в gnulib ).

В результате есть дополнительный шаг: для некоторых систем, где HAVE_BUG_BIG_NANOSLEEP равен true, сон усекается до 24 дней, а затем вызывается в цикле. Это касается некоторых (или всех?) Дистрибутивов Linux. Обратите внимание, что эта оболочка может не использоваться, если тест configure -time успешен ( source ).

В частности, это будет 24 * 24 * 60 * 60 = 2073600 seconds (плюс 999999999 наносекунд); но это вызывается в цикле для соблюдения указанного общего времени ожидания. Поэтому предыдущие выводы остаются в силе.


В заключение, результирующее время сна не бесконечно, но достаточно велико для всех практических целей , даже если результирующий фактический промежуток времени не является переносимым; это зависит от ОС и архитектуры.

Чтобы ответить на исходный вопрос, это, очевидно, достаточно хорошо, но если по какой-то причине ( очень ограниченная ресурсами система) вы действительно хотите избежать бесполезного дополнительного таймера обратного отсчета, я думаю, что наиболее правильная альтернатива это использовать метод cat, описанный в других ответах.

8 голосов
/ 25 ноября 2014

sleep infinity выглядит наиболее элегантно, но иногда по какой-то причине не работает. В этом случае вы можете попробовать другие команды блокировки, такие как cat, read, tail -f /dev/null, grep a и т. Д.

5 голосов
/ 29 января 2011

А как насчет отправки SIGSTOP себе?

Это должно приостановить процесс до получения SIGCONT. Что в вашем случае: никогда.

kill -STOP "$$";
# grace time for signal delivery
sleep 60;
2 голосов
/ 28 ноября 2017

У меня недавно была необходимость сделать это. Я придумал следующую функцию, которая позволит bash спать вечно без вызова какой-либо внешней программы:

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

ПРИМЕЧАНИЕ. Ранее я публиковал эту версию, в которой каждый раз открывался и закрывался дескриптор файла, но обнаружил, что в некоторых системах, выполняющих эту операцию сотни раз в секунду, в конечном итоге происходит блокировка. Таким образом, новое решение сохраняет дескриптор файла между вызовами функции. Bash все равно очистит его при выходе.

Это можно вызвать так же, как / bin / sleep, и он будет спать в течение запрошенного времени. Вызывается без параметров, будет зависать вечно.

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

В моем блоге есть рецензия с чрезмерными подробностями

0 голосов
/ 26 июня 2019

Этот подход не потребляет ресурсов для поддержки процесса.

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

Разбивка

  • while :; do sleep 1; done & Создает фиктивный процесс в фоновом режиме
  • kill -STOP $! Останавливает фоновый процесс
  • wait $! Подождите, пока фоновый процесс будет заблокирован навсегда, потому что фоновый процесс был остановлен до
0 голосов
/ 02 июня 2016
while :; do read; done

нет ожидания процесса сна ребенка.

0 голосов
/ 29 мая 2010

Вместо того, чтобы убивать оконный менеджер, попробуйте запустить новый с --replace или -replace, если доступно.

...