배쉬 : 무한 수면 (무한 차단)


158

startx내 X를 평가하는 데 사용 합니다 .xinitrc. 내 .xinitrc에서을 사용하여 창 관리자를 시작 /usr/bin/mywm합니다. 이제 WM을 죽이면 (다른 WM을 테스트하기 위해) .xinitrc스크립트가 EOF에 도달 했기 때문에 X도 종료됩니다 . 그래서 나는 이것을 내 끝에 추가했다 .xinitrc:

while true; do sleep 10000; done

이렇게하면 WM을 죽이면 X가 종료되지 않습니다. 이제 내 질문 : 루핑 수면 대신 무한 수면을 어떻게 할 수 있습니까? 스크립트를 정지시키는 것과 같은 명령이 있습니까?

친애하는

답변:


330

sleep infinity 고양이 학대없이 제안하고 작동하는 것을 정확하게 수행합니다.


16
멋있는. 불행히도 내 busybox는 이해하지 못합니다.
사용자가 아닌 사용자

12
BSD (또는 최소한 OS X)도 sleep infinityLinux에 대해 배우는 것이 좋지만 이해하지 못합니다 . 그러나 while true; do sleep 86400; done적절한 대체물이어야합니다.
Ivan X

16
이와 관련하여 별도의 답변으로 문서화 한 연구를했습니다. 요약하면 infinityC에서 "string"에서 a로 변환됩니다 double. 그런 다음 double허용되는 최대 값으로 잘립니다 timespec. 이는 매우 많은 시간 (아키텍처에 따라 다름)을 의미하지만 이론 상으로는 유한합니다.
jp48

72

tail 차단하지 않습니다

항상 그렇듯이 : 모든 것에 대해 짧고 이해하기 쉽고 따르기 쉽고 완전히 잘못된 대답이 있습니다. 여기이 tail -f /dev/null범주에 속합니다.)

당신이 그것을 보시면 strace tail -f /dev/null이 솔루션은 차단되지 않습니다! sleepLinux와 같은 inotify시스템 과 같은 귀중한 리소스를 사용하기 때문에 문제 의 솔루션 보다 훨씬 나쁩니다 . 또한 루프 를 /dev/null만들기 위해 쓰는 다른 프로세스 tail. (Ubuntu64 16.10에서는 이미 사용량이 많은 시스템에서 초당 10 회의 syscall이 추가됩니다.)

문제는 차단 명령이었습니다

불행히도, 그런 것은 없습니다 ..

읽는다 : 나는 이것을 쉘로 직접 보관할 방법을 모른다.

모든 sleep infinity신호는 심지어 일부 신호에 의해 중단 될 수 있습니다. 따라서 예외적으로 반환되지 않는지 확인하려면 이미 수행 한 것처럼 루프에서 실행해야합니다 sleep. (Linux의 경우) /bin/sleep24 일에 한도를 정했다는 점을 명심하십시오 ( strace sleep infinity따라서 살펴보십시오 ).

while :; do sleep 2073600; done

(내가 sleep24 일보다 높은 값을 위해 내부적으로 루프를 믿지만 이것은 차단하지 않고 매우 느리게 반복됩니다.이 루프를 외부로 옮기지 않는 이유는 무엇입니까?)

.. 그러나 당신은 이름 없는와 꽤 가까이 올 수 있습니다 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와 a를 열어 두어야합니다 fifo.

명명 된 변형 fifo

named를 사용하지 않으면 fifo다음과 같이 할 수 있습니다.

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

읽기에 루프를 사용하지 않는 것은 약간 느슨하지만, fifo원하는만큼 자주 재사용 하고 reads를 사용하여 터미널을 사용할 수 있습니다 touch "$HOME/.pause.fifo"(하나 이상의 읽기 대기가있는 경우 모든 것이 한 번에 종료 됨).

또는 Linux pause()syscall을 사용하십시오.

무한 차단을 위해 리눅스 커널 호출이 있는데, pause()우리가 원하는 것을한다 : 신호가 도착할 때까지 영원히 기다린다. 그러나 이것에 대한 사용자 공간 프로그램은 아직 없습니다.

그러한 프로그램을 만드는 것은 쉽습니다. 여기라는 매우 작은 리눅스 프로그램을 만들 수있는 조각이다 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 하나가 해제됩니다. 일부 IO 리디렉션을 통해 솔루션을 개선하여 사용하지 않는 FD를 해제 할 수도 있습니다. 이는 사용자에게 달려 있습니다.)

작동 방식 (제 생각) : ctypes.CDLL(None)표준 C 라이브러리를로드하고 pause()추가 루프 내 에서 함수를 실행합니다 . C 버전보다 효율적이지 않지만 작동합니다.

당신을위한 나의 추천 :

반복 수면을 유지하십시오. 이해하기 쉽고 휴대 성이 뛰어나며 대부분의 시간을 차단합니다.


1
@Andrew 일반적으로 trap(쉘의 동작을 신호로 수정하는) 또는 배경 (쉘이 Strg + C와 같이 터미널의 신호를 차단할 수 있도록)이 필요하지 않습니다 . 이것 sleep infinity으로 충분합니다 ( exec sleep infinity마지막 문장 인 것처럼 작동 합니다. 차이 사용을 보려면 strace -ffDI4 bash -c 'YOURCODEHERE'). sleep특정 상황에서 돌아올 수 있기 때문에 반복 수면이 더 좋습니다 . 예를 들어 , 수면 루프 대신 끝나기 killall sleep때문에 X에서 X11이 갑자기 종료되는 것을 원하지 않습니다 . .xstartupsleep infinity
Tino

약간 모호 할 수 있지만 선택적으로 다양한 신호를 무시하고 s6-pause실행하는 userland 명령 pause()입니다.
Patrick

/bin/sleep당신이 말하는 것처럼 @Tino 는 24 일에 제한이 없습니다. 업데이트 할 수 있다면 좋을 것입니다. Linux에서 지금 이 코드 는 활성화되어 있습니다. 개별 nanosleep()syscall을 24 일로 제한하지만 루프로 호출합니다. 따라서 sleep infinity24 일 후에는 종료하지 않아야합니다. double양의 무한대가 변환됩니다 struct timespec. rpl_nanosleepGDB를 살펴보면 우분투 16.04 infinity로 변환됩니다 { tv_sec = 9223372036854775807, tv_nsec = 999999999 }.
nh2

@ nh2 본문에서 이미 잠을 완전히 막지 않고 반복되는 것으로 언급되어 있습니다. 이 사실을 좀 더 명확하게하기 위해 약간 편집했습니다. 혼자서 컴파일 된 루핑 코드가 실제로 있다는 사실을 증명할 수 없기 때문에이 " 아마도 " 주의하십시오 .24 일 동안이 테스트 (또는 디 컴파일 ) 를 기다리는 것을 원하지 않습니다 . 어려운 수학적 증거가 없다면 무언가 무언가가 실제로있는 것처럼 방어 적으로 프로그램하는 것이 좋습니다. 또한 아무것도 신뢰하지 마십시오 :stracesleep/bin/sleepkillall -9 sleep
Tino

pause () 옵션은 perl을 사용하여 쉽게 수행 할 수 있습니다 : perl -MPOSIX -e 'pause ()'
tgoodhart

70

어쩌면 이것이 못생긴 것처럼 보이지만 실행을 cat계속하고 입력을 영원히 기다리게 하지 않는 이유는 무엇입니까?


4
읽을 교수형 파이프가 없으면 작동하지 않습니다. 조언 부탁드립니다.
매트 소목 장이

2
@ 매트, 어쩌면 파이프를 cat만들어? mkfifo pipe && cat pipe
Michał Trybus

@twalberg가 말한 내용이지만, 여기에 표시된대로 즉시 3에 다시 할당하고 연결을 해제 할 수 있습니다. superuser.com/a/633185/762481
jp48

32

TL; DR : sleep infinity실제로 허용 된 최대 시간을 잠들게합니다.

왜 이것이 어디에도 문서화되어 있지 않은지 궁금 해서 GNU coreutils 에서 소스 를 읽으려고 노력했으며 대략 다음과 같이 실행됩니다.

  1. strtod'무한대'를 배정도로 변환하려면 첫 번째 인수에서 C stdlib에서 사용하십시오 . 따라서 IEEE 754 배정도를 가정하면 64 비트 양의 무한대 값이 seconds변수에 저장됩니다 .
  2. 호출은 xnanosleep(seconds)( gnulib에서 발견 )이 차례를 발동에 dtotimespec(seconds)( 도 gnulib의 ) 변환하는 방법 doublestruct timespec.
  3. struct timespec정수 부분 (초)과 소수 부분 (나노초)의 숫자 쌍입니다. 양의 무한대 를 정수 로 순전히 변환 하면 정의되지 않은 동작이 발생하고 (C 표준의 §6.3.1.4 참조) 대신로 절단됩니다 TYPE_MAXIMUM (time_t).
  4. 실제 값은 TYPE_MAXIMUM (time_t)표준에 설정되어 있지 않습니다 ( sizeof(time_t)그렇지 않더라도). 예를 들어 최근 Linux 커널에서 x86-64를 선택하겠습니다.

이것은 TIME_T_MAXLinux 커널에 있으며 다음과 같이 ( time.h)로 정의 됩니다.

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

참고 time_t__kernel_time_ttime_t입니다 long; LP64 데이터 모델이 사용되므로 sizeof(long)8 (64 비트)도 사용됩니다.

결과 : TIME_T_MAX = 9223372036854775807.

sleep infinite, 실제 절전 시간은 9223372036854775807 초 (10 ^ 11 년)입니다. 그리고 32 비트 리눅스 시스템의 경우 ( sizeof(long)4 (32 비트)) : 2147483647 초 (68 년; 2038 년 문제 도 참조하십시오 ).


편집 : 분명히 nanoseconds호출 된 함수는 직접 syscall이 아니라 OS 종속 래퍼 ( gnulib에 정의되어 있음 )입니다.

그 결과로 추가 단계가 있습니다 : 일부 시스템 HAVE_BUG_BIG_NANOSLEEPtrue수면 24 일립니다 다음 루프에서했다. 이것은 일부 (또는 모두?) Linux 배포판의 경우입니다. configure- time 테스트가 성공한 경우 ( source ) 이 랩퍼를 사용할 수 없습니다 .

특히, 24 * 24 * 60 * 60 = 2073600 seconds(99999999 나노초 플러스); 그러나 지정된 총 절전 시간을 준수하기 위해 루프에서 호출됩니다. 따라서 이전 결론은 유효합니다.


결론적 으로 , 결과적인 실제 시간 경과가 휴대용이 아닌 경우에도 결과적인 수면 시간 은 무한하지만 모든 실제적인 목적을 위해 충분히 높을 수 없다. OS와 아키텍처에 따라 다릅니다.

원래 질문에 대답하기에, 이것은 분명히 충분하지만 어떤 이유로 ( 매우 자원이 제한된 시스템) 쓸모없는 여분의 카운트 다운 타이머를 피하고 싶다면 가장 적합한 대안은 cat다른 답변에 설명 된 방법 을 사용하는 것입니다 .


1
다음로 coreutils에서, sleep infinity지금 실제로 반복하지 않고 영원히 잠됩니다 lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
블라디미르 Panteleev

8

sleep infinity가장 우아해 보이지만 때로는 어떤 이유로 작동하지 않습니다. 이 경우, 다른 차단 명령과 같은 시도 할 수 있습니다 cat, read, tail -f /dev/null, grep a


1
tail -f /dev/null또한 SaaS 플랫폼에서 작동하는 솔루션이었습니다
schmunk

2
tail -f /dev/nullstdin을 소비하지 않는 이점도 있습니다. 그런 이유로 사용했습니다.
Sudo Bash

이 옵션을 고려하는 사람들은이 옵션 의 결과에 대해 배우 려면 이 답변 을 읽어야 합니다 .
Shadow

6

SIGSTOP 을 자신 에게 보내는 것은 어떻습니까?

SIGCONT가 수신 될 때까지 프로세스를 일시 정지해야합니다. 당신의 경우에는 : 결코.

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

6
신호는 비동기 적입니다. 따라서 다음과 같은 상황이 발생할 수 있습니다. a) 쉘 호출 kill b) kill은 쉘에게 STOP 신호를 수신해야 함을 알려줍니다. c) kill이 종료되고 쉘로 돌아갑니다. d) 쉘이 계속됩니다 (스크립트가 종료되어 종료 될 수 있습니다) 신호 STOP은 쉘
하지 - 한 - 사용자

1
@temple 위대한 통찰력은 신호의 비동기 특성에 대해 생각하지 않았습니다. 감사!
michuelnik

4

sleep infinity문서화되지 않았지만 작동 하는지 설명하겠습니다 . jp48의 답변 도 유용합니다.

가장 중요한 것 : inf또는 infinity(대소 문자를 구분하지 않음) 을 지정 하면 구현에서 허용하는 가장 긴 시간 동안 (예 : HUGE_VAL및 값이 더 작은) 휴면 상태가 될 수 있습니다 TYPE_MAXIMUM(time_t).

이제 세부 사항을 파헤쳐 보자. sleep명령 소스 코드는 coreutils / src / sleep.c 에서 읽을 수 있습니다 . 기본적으로 함수는 다음을 수행합니다.

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

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

xstrtod()

gnulib / lib / xstrtod.c 에 따르면 , 호출은 변환 함수를 사용하여 xstrtod()문자열 argv[i]을 부동 소수점 값 *s으로 변환하여 저장합니다 cl_strtod().

cl_strtod()

에서 볼 수 있듯이 로 coreutils / lib 디렉토리 / CL-strtod.c , cl_strtod()사용, 부동 소수점 값에 문자열로 변환합니다 strtod().

strtod()

에 따르면 man 3 strtod, strtod()유형의 값에 문자열로 변환합니다 double. 맨 페이지 말한다

(문자열의 초기 부분)의 예상 형식은 ... 또는 (iii) 무한대 또는 ...

무한대는

무한대는 대소 문자를 무시하고 "INF"또는 "INFINITY"입니다.

문서가 알려 주지만

올바른 값으로 오버플로가 발생하면 더하기 또는 빼기 HUGE_VAL( HUGE_VALF, HUGE_VALL)가 반환됩니다.

무한대가 어떻게 취급되는지는 명확하지 않다. 소스 코드 gnulib / lib / strtod.c를 보자 . 우리가 읽고 싶은 것은

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

따라서, INFINFINITY(대소 문자 구별)는 다음과 같이 간주된다 HUGE_VAL.

HUGE_VAL 가족

N1570 을 C 표준으로 사용합시다 . HUGE_VAL, HUGE_VALFHUGE_VALL매크로에 정의되어 §7.12-3

매크로
    HUGE_VAL
는 양의 상수 상수로 확장되며 반드시 부동 소수점으로 표현할 필요는 없습니다. 매크로
    HUGE_VALF
    HUGE_VALL
는 각각 부동 및 긴 이중 아날로그입니다 HUGE_VAL.

HUGE_VAL, HUGE_VALFHUGE_VALL인피니티를 지원하는 구현에서 긍정적 인 인피니티 일 수 있습니다.

§7.12.1-5

부동 결과가 오버 플로우 및 기본 반올림이 적용되는 경우,이 함수는 매크로의 값을 반환 HUGE_VAL, HUGE_VALF또는 HUGE_VALL반환 형식에 따라

이해 xnanosleep (s)

이제 우리는의 모든 본질을 이해합니다 xstrtod(). 위의 설명에서, xnanosleep(s)우리가 실제로 본 것은 실제로 의미 하는 것은 명백합니다 xnanosleep(HUGE_VALL).

xnanosleep()

소스 코드 gnulib / lib / xnanosleep.c에 따르면 xnanosleep(s)본질적으로 다음을 수행합니다.

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

dtotimespec()

이 함수는 type 인수를 type double객체 로 변환합니다 struct timespec. 매우 간단하므로 소스 코드 gnulib / lib / dtotimespec.c를 인용 하겠습니다 . 모든 의견은 저에 의해 추가됩니다.

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

이후 time_t일체형으로 정의된다 (§7.27.1-3 참조), 우리가 유형의 최대 값이 가정 자연 time_t보다 작다 HUGE_VAL(유형 double우리가 오버 플로우하는 경우 입력 수단). (실제로이 절차는 본질적으로 동일하기 때문에이 가정은 필요하지 않습니다.)

make_timespec()

우리가 올라 가야하는 마지막 벽은 make_timespec()입니다. 운 좋게도 소스 코드 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

나는 최근에 이것을 할 필요가 있었다. 외부 프로그램을 호출하지 않고 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

내 블로그에 과도한 세부 정보가있는 글이 있습니다.


1

이 방법은 프로세스를 유지하기 위해 리소스를 소비하지 않습니다.

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

고장

  • while :; do sleep 1; done & 백그라운드에서 더미 프로세스를 만듭니다.
  • kill -STOP $! 백그라운드 프로세스를 중지합니다
  • wait $! 백그라운드 프로세스를 기다립니다.이 프로세스는 영원히 차단되므로 백그라운드 프로세스가 중지되기 전에 발생합니다.


-2
while :; do read; done

어린이 수면 과정을 기다리지 않아도됩니다.


1
이것이 stdin여전히에 연결되어 있으면 먹는다 tty. 당신이 < /dev/null그것을 바쁜 루프로 실행하면 . 특정 상황에서 일부 사용 될 수 있으므로 공감하지 않습니다.
티노

1
이것은 매우 나쁜 생각입니다. 모든 CPU를 소비합니다.
Mohammed Noureldin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.