Bash: бесконечный сон (бесконечная блокировка)

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

while true; do sleep 10000; done

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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 используя touch "$HOME/.pause.fifo" (если touch "$HOME/.pause.fifo" более одного чтения, все прекращаются сразу).

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

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

С

Создать такую программу легко. Вот фрагмент кода для создания очень маленькой программы для 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, но работает.

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

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

Ответ 4

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

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

  1. Используйте strtod из C stdlib для первого аргумента, чтобы преобразовать бесконечность в двойную точность. Таким образом, при условии двойной точности IEEE 754 64-битное положительное значение бесконечности сохраняется в переменной seconds.
  2. 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 функция nanoseconds - это не системный вызов, а зависимая от ОС оболочка (также определенная в gnulib).

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

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


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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

Здесь на моем блоге написано много подробностей

Ответ 8

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

Ответ 9

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

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

Сломать

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

Ответ 10

while :; do read; done

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