Bash: sonsuz uyku (sonsuz engelleme)


158

Ben startxX'i başlatmak için kullanıyorum .xinitrc. Benim içinde .xinitrcI kullanarak pencere yöneticisini başlatın /usr/bin/mywm. Şimdi, WM'mi öldürürsem (başka bir WM'yi test etmek için), .xinitrckomut dosyası EOF'a ulaştığı için X de sona erer . Bu yüzden benim sonuna ekledim .xinitrc:

while true; do sleep 10000; done

WM'mi öldürürsem X bu şekilde sonlanmayacak. Şimdi sorum: nasıl sonsuz bir uyku yapabilirim döngüsünü kullanmak yerine ? Senaryoyu dondurmak gibi bir komut var mı?

Saygılarımla

Yanıtlar:


330

sleep infinity tam olarak ne önerdiğini yapar ve kedi istismarı olmadan çalışır.


16
Güzel. Maalesef meşgul kutum anlamıyor.
kullanıcı olmayan

12
BSD (veya en azından OS X) de anlamıyor sleep infinity, ancak Linux için öğrenmek harika bir şeydi. Ancak, while true; do sleep 86400; doneyeterli bir yedek olmalıdır.
Ivan X

16
Bununla ilgili olarak ayrı bir cevapta belgelediğim bazı araştırmalar yaptım. Özetlemek gerekirse: infinityC olarak "string" den a 'ya dönüştürülür double. Daha sonra bu doubleizin verilen maksimum değerlere kısaltılır timespec, bu da çok fazla miktarda (mimariye bağlı), ancak teoride sonlu anlamına gelir.
jp48

72

tail engellemez

Her zamanki gibi: Her şey için kısa, anlaşılması kolay, takip edilmesi kolay ve tamamen yanlış bir cevap var. İşte tail -f /dev/nullbu kategoriye girer;)

Eğer ona bakarsanız, strace tail -f /dev/nullbu çözümün bloke olmaktan uzak olduğunu fark edeceksiniz! Sistem sleepgibi değerli kaynakları (Linux altında) kullandığı için muhtemelen sorunun çözümünden bile daha kötüdür inotify. Ayrıca döngü /dev/nullyapmak için yazma diğer süreçler tail. (Ubuntu64 16.10 cihazımda, zaten meşgul bir sistemde saniyede birkaç sistem çağrısı ekleniyor.)

Soru engelleme komutuydu

Maalesef böyle bir şey yok ..

Oku: Bunu doğrudan kabukla arşivlemenin bir yolunu bilmiyorum.

Her şey (çift sleep infinity) bir sinyalle kesilebilir. Bu yüzden son derece geri dönmediğinden emin olmak istiyorsanız, sizin için zaten yaptığınız gibi bir döngüde çalışmalıdır sleep. (Linux'ta) /bin/sleepgörünüşe göre 24 günde kapatıldığını (bir göz atın strace sleep infinity), bu nedenle muhtemelen yapabileceğiniz en iyisi:

while :; do sleep 2073600; done

( sleepİçeride 24 günden daha yüksek değerler için döngüler olduğuna inanıyorum , ancak bu şu anlama geliyor: Engellemiyor, çok yavaş döngü yapıyor. Öyleyse neden bu döngüyü dışarıya taşımıyorsunuz?)

.. ama adsız biriyle oldukça yaklaşabilirsiniz fifo

İşleme gönderilen sinyal olmadığı sürece gerçekten engelleyen bir şey oluşturabilirsiniz. Aşağıdaki kullanımlar bash 4, 2 PID ve 1 fifo:

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

İsterseniz bunun gerçekten engellenip engellenmediğini kontrol edebilirsiniz strace:

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

Bu nasıl inşa edildi

readgiriş verisi yoksa engeller (başka cevaplara bakınız). Ancak, tty(aka. stdin) Kullanıcı oturumu kapattığında kapatıldığı için genellikle iyi bir kaynak değildir. Ayrıca bazı girişleri çalabilir tty. İyi değil.

readBlok yapmak için fifo, hiçbir şey döndürmeyecek bir şey beklememiz gerekir . Gelen bash 4aynen böyle a bize bir komut vardır fifo: coproc. Eğer read(ki bizim coproc) engelleme de beklersek , biz yapılır. Ne yazık ki bunun iki PID ve a fifo.

Adlandırılmış bir varyant fifo

Bir adlandırılmış kullanarak rahatsız fifoetmezseniz, bunu aşağıdaki gibi yapabilirsiniz:

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

Okumada bir döngü kullanmamak biraz özensizdir, ancak bunu istediğiniz fifosıklıkta yeniden kullanabilir ve reads terminatını kullanaraktouch "$HOME/.pause.fifo" (tek bir okuma beklemeden daha fazlası varsa, hepsi aynı anda sonlandırılır).

Veya Linux pause()sistem çağrısını kullanın

Sonsuz engelleme için, pause()istediğimiz şeyi yapan bir Linux çekirdek çağrısı var : Sonsuza kadar bekleyin (bir sinyal gelene kadar). Ancak bunun için henüz bir kullanıcı alanı programı bulunmamaktadır.

C

Böyle bir program oluşturmak kolaydır. İşte pausesüresiz olarak duraklatan (ihtiyaçlar diet, gccvb.) Adlı çok küçük bir Linux programı oluşturmak için bir snippet :

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

python

Kendiniz bir şey derlemek istemiyorsanız, ancak pythonyüklediyseniz, bunu Linux altında kullanabilirsiniz:

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

(Not: Kullanın exec python -c ... Mevcut kabuğu değiştirmek için , bu bir PID'yi serbest bırakır. Çözüm, bazı IO yönlendirmeleriyle de geliştirilebilir, kullanılmayan FD'leri serbest bırakır. Bu size kalmış.)

Bu nasıl çalışır (Sanırım): ctypes.CDLL(None)standart C kitaplığını yükler ve pause()bazı ek döngü içinde işlevini çalıştırır . C sürümünden daha az verimli, ancak çalışıyor.

Size tavsiyem:

Döngü uykusunda kalın. Anlaması kolay, çok taşınabilir ve çoğu zaman engeller.


1
@Andrew Normalde trap(kabuğun davranışını sinyallere değiştiren) veya arka plana (kabuğun terminalden sinyalleri, Strg + C gibi kesmesine izin veren) ihtiyacınız yoktur. Bu yüzden sleep infinityyeterlidir ( exec sleep infinityson ifadeyi kullanıyormuş gibi davranır . Fark kullanımını görmek için strace -ffDI4 bash -c 'YOURCODEHERE'). Döngü uykusu daha iyidir, çünkü sleepbelirli durumlarda geri dönebilir. Örneğin, X11'in bir uyku döngüsünün yerine bitmesi killall sleepnedeniyle aniden kapanmasını istemezsiniz . .xstartupsleep infinity
Tino

Biraz belirsiz olabilir, ancak isteğe bağlı olarak çeşitli sinyalleri yok sayarak, s6-pausekullanıcı tarafından yönetilen bir komuttur pause().
Patrick

@Tino /bin/sleep, söylediğiniz gibi 24 günde sınırlandırılmamıştır. Bunu güncelleyebilirsen iyi olur. Linux'ta şu anda bu kod etkindir. Bireysel nanosleep()sistem çağrılarını 24 güne kadar kapsar, ancak bir döngüde çağırır. Bu yüzden sleep infinity24 gün sonra çıkmamalısınız. doublePozitif sonsuzluk bir dönüştürülür alır struct timespec. rpl_nanosleepGDB'ye bakarak, Ubuntu 16.04'e infinitydönüştürülür { tv_sec = 9223372036854775807, tv_nsec = 999999999 }.
nh2

@ nh2 Metinde, tam olarak bloke etmek yerine büyük olasılıkla uyku döngülerinden bahsedilmişti . Umarım bu gerçeği biraz daha açıklığa kavuşturmak için biraz düzenledim. Bu " muhtemelen " lütfen unutmayın , çünkü stracetek başına gerçekten derlenmiş bazı döngü kodu olduğunu kanıtlayamıyoruz sleep, ve ben sadece bunu test etmek (ya da koda /bin/sleep) 24 gün beklemek istemiyorum . Sert bir matematiksel kanıt yoksa, bir şeyin gerçekten göründüğü gibi olduğunu savunmakla programlamak her zaman daha iyidir. Ayrıca hiçbir şeye asla güvenme:killall -9 sleep
Tino

Pause () seçeneği perl ile oldukça kolay bir şekilde yapılabilir: perl -MPOSIX -e 'pause ()'
tgoodhart

70

Belki bu çirkin gözüküyor, ama neden sadece koşup catsonsuza dek girişi beklemesin?


4
Okumak için asılı bir borunuz yoksa bu işe yaramaz. Tavsiye lütfen.
Matt Joiner

2
@ Matt, belki bir pipo yap, ve cato? mkfifo pipe && cat pipe
Michał Trybus

@Twalberg ne diyor, ancak ek olarak burada gösterildiği gibi hemen 3'e yeniden atayabilir ve bağlantısını kaldırabilirsiniz: superuser.com/a/633185/762481
jp48

33

TL; DR: sleep infinityaslında izin verilen maksimum süreyi uyuyor, bu da sonlu.

Bunun neden hiçbir yerde belgelenmediğini merak ederek , GNU coreutils'in kaynaklarını okumak için uğraştım ve kabaca aşağıdakileri yürüttüğünü buldum:

  1. strtod'Sonsuzluğu' çift hassasiyete dönüştürmek için ilk argümandaki C stdlib'den kullanın . Bu nedenle, IEEE 754'ün çift duyarlıklı olduğu varsayıldığında, 64 bit pozitif sonsuzluk değeri secondsdeğişkende saklanır .
  2. Çağırır xnanosleep(seconds)( gnulib bulunan ), bu dönüş başlatır içinde dtotimespec(seconds)( aynı zamanda gnulib olarak ) dönüştürmek için doubleiçin struct timespec.
  3. struct timespecsadece bir çift sayıdır: tamsayı kısım (saniye cinsinden) ve kesirli kısım (nanosaniye cinsinden). Pozitif sonsuzluğu tam sayıya dönüştürmek , tanımsız davranışa neden olur (C standardından §6.3.1.4'e bakın), bunun yerine kısalır TYPE_MAXIMUM (time_t).
  4. Gerçek değeri TYPE_MAXIMUM (time_t)standartta ayarlanmamıştır (hatta ayarlanmamıştır sizeof(time_t)); yani, uğruna son Linux çekirdeğinden x86-64 seçelim.

Bu, TIME_T_MAX( time.h) olarak tanımlanan Linux çekirdeğinde bulunur :

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

Not time_tolduğu __kernel_time_tve time_tolduğu long; LP64 veri modeli kullanılır, sizeof(long)8 (64 bit) de kullanılır.

Hangi sonuçları: TIME_T_MAX = 9223372036854775807.

Yani: sleep infinite9223372036854775807 saniyelik (10 ^ 11 yıl) gerçek uyku süresiyle sonuçlanır. Ve 32 bit linux sistemleri için ( sizeof(long)4 (32 bit)): 2147483647 saniye (68 yıl; ayrıca bkz . 2038 yılı sorunu ).


Düzenleme : Görünüşe göre nanosecondsçağrılan işlev doğrudan syscall değil, bir işletim sistemine bağımlı sarıcı ( gnulib olarak da tanımlanır ).

Sonuç olarak ekstra bir adım var: Bazı sistemler için HAVE_BUG_BIG_NANOSLEEPolan trueuyku 24 güne kısaltılır ve daha sonra bir döngüde çağırdı. Bazı Linux dağıtımları için bu geçerlidir. Configure- time testi başarılı olursa ( kaynak ) bu paketin kullanılamayabileceğini unutmayın .

Özellikle, bu 24 * 24 * 60 * 60 = 2073600 seconds(artı 999999999 nanosaniye) olacaktır; ancak bu, belirtilen toplam uyku süresine uymak için bir döngü içinde çağrılır. Dolayısıyla önceki sonuçlar geçerliliğini korumaktadır.


Sonuç olarak, ortaya çıkan uyku süresi sonsuz değildir, ancak gerçek zaman aşımı taşınabilir olmasa bile, tüm pratik amaçlar için yeterince yüksektir ; işletim sistemine ve mimariye bağlıdır.

Orijinal soruyu cevaplamak için, bu yeterince iyi ama bazı nedenlerden dolayı ( çok kaynak kısıtlı bir sistem) gerçekten gereksiz bir geri sayım sayacından kaçınmak istiyorsanız, en doğru alternatif, catdiğer cevaplarda açıklanan yöntemi kullanmaktır. .


1
Bir sonraki çekirdeklerde, sleep infinityşimdi döngü olmadan sonsuza kadar uyuyacak: lists.gnu.org/archive/html/bug-gnulib/2020-02/msg00081.html
Vladimir Panteleev

8

sleep infinityen zarif görünüyor, ancak bazen bir nedenden dolayı çalışmıyor. Bu durumda, diğer engelleme komutları gibi deneyebilirsiniz cat, read, tail -f /dev/null, grep avb


1
tail -f /dev/nullbir SaaS platformunda da benim için çalışan bir çözümdü
schmunk

2
tail -f /dev/nullayrıca stdin tüketmeme avantajına sahiptir. Ben bu nedenle kullandım.
Sudo Bash

Bu seçeneği düşünenler , bu seçeneğin sonuçları hakkında bilgi edinmek için bu cevabı okumalıdır .
Gölge

6

Kendisine bir SIGSTOP göndermeye ne dersiniz ?

Bu, SIGCONT alınana kadar işlemi duraklatmalıdır. Sizin durumunuzda: asla.

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

6
Sinyaller zaman uyumsuzdur. Böylece şunlar olabilir: a) kabuk çağrıları öldürme b) öldürme çekirdeğe kabuğun STOP sinyali alacağını söyler kabuk STOP sinyali
kullanıcı değil

1
@temple Büyük içgörü, sinyallerin asenkron doğasını düşünmedi. Teşekkürler!
michuelnik

4

sleep infinityBelgelendirilmemiş olsa da neden işe yaradığını açıklayayım . jp48'in yanıtı da faydalıdır.

En önemli şey: belirterek infveya infinity(küçük harf duyarsız ikisi), sen en uzun süre uygulamanız izinleri (yani daha küçük değeri için uyuyabilir HUGE_VALve TYPE_MAXIMUM(time_t)).

Şimdi ayrıntıları inceleyelim. sleepKomutun kaynak kodu coreutils / src / sleep adresinden okunabilir . C. Temel olarak, işlev bunu yapar:

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

anlayış xstrtod (argv[i], &p, &s, cl_strtod)

xstrtod()

Göre gnulib / lib / xstrtod.c , çağrı xstrtod()dönüştürür dize argv[i]bir kayan noktalı değer ve almak stok *s, bir dönüştürme işlevini kullanarak cl_strtod().

cl_strtod()

Dan görülebileceği gibi coreutils / lib / cl-strtod.c , cl_strtod()kullanarak, bir kayan nokta değeri bir dize dönüştürür strtod().

strtod()

Buna göre man 3 strtod, strtod()bir dizeyi tür değerine dönüştürür double. Manpage diyor

Dizenin (başlangıç ​​kısmı) beklenen şekli ... veya (iii) bir sonsuzluk veya ...

ve sonsuzluk şu şekilde tanımlanır:

Bir sonsuzluk, durumu dikkate almayan "INF" veya "INFINITY" dir.

Her ne kadar belge

Doğru değer taşmaya neden olursa artı veya eksi HUGE_VAL( HUGE_VALF, HUGE_VALL) döndürülür

, bir sonsuzluğun nasıl tedavi edildiği açık değildir. Şimdi gnulib / lib / strtod.c kaynak kodunu görelim . Okumak istediğimiz şey

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

Bu nedenle INFve INFINITY(her ikisi de büyük / küçük harfe duyarlı olmayan) olarak kabul edilir HUGE_VAL.

HUGE_VAL aile

En kullanalım N1570 C standart olarak. HUGE_VAL, HUGE_VALFVe HUGE_VALLmakrolar tanımlanır §7.12-3

Makro
    HUGE_VAL
, bir kayan nokta olarak gösterilmesi gerekmeyen pozitif bir çift sabit ifadeye genişler. Makrolar
    HUGE_VALF
    HUGE_VALL
sırasıyla float ve uzun çift analoglarıdır HUGE_VAL.

HUGE_VAL, HUGE_VALFve HUGE_VALLsonsuzlukları destekleyen bir uygulamada pozitif sonsuzluklar olabilir.

ve §7.12.1-5'te

Yüzen bir sonucudur taşıyor ve varsayılan yuvarlama yürürlükte ise, o işlev makro değerini verir HUGE_VAL, HUGE_VALFya da HUGE_VALLgeri dönüş tipine göre

anlayış xnanosleep (s)

Şimdi tüm özünü anlıyoruz xstrtod(). Yukarıdaki açıklamalardan, xnanosleep(s)ilk gördüğümüz şey aslında berraktır xnanosleep(HUGE_VALL).

xnanosleep()

Gnulib / lib / xnanosleep.c kaynak koduna göre , xnanosleep(s)esas olarak bunu yapar:

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

dtotimespec()

Bu işlev tür argümanını tür doublenesnesine dönüştürür struct timespec. Çok basit olduğu için, gnulib / lib / dtotimespec.c kaynak kodunu göstereyim . Tüm yorumlar benim tarafımdan eklendi.

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

Yana time_ttamamlayıcı türü olarak tanımlanan (§7.27.1-3 bakınız), biz türü maksimum değeri kabul doğal time_tdaha küçük olan HUGE_VAL(Çeşidi doubletaşmanın durumda girdiği anlamına gelir). (Aslında bu varsayım gerekli değildir, çünkü her durumda prosedür esasen aynıdır.)

make_timespec()

Tırmanmamız gereken son duvar make_timespec(). Neyse ki, gnulib / lib / timespec.h kaynak kodundan alıntı yapmak yeterlidir.

_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

Son zamanlarda bunu yapmaya ihtiyacım vardı. Ben bash herhangi bir harici program çağırmadan sonsuza kadar uyku sağlayacak aşağıdaki fonksiyonu ile geldi:

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

NOT: Daha önce bunun her zaman dosya tanımlayıcısını açıp kapatacak bir sürümünü yayınladım, ancak bunu yapan bazı sistemlerde yüzlerce kez bir saniye sonunda kilitleneceğini buldum. Bu nedenle yeni çözüm, dosya tanımlayıcısını işleve yapılan çağrılar arasında tutar. Bash yine de çıkışta temizler.

Bu / bin / sleep gibi çağrılabilir ve istenen süre boyunca uyuyacaktır. Parametresiz olarak adlandırıldığında, sonsuza kadar askıda kalacaktır.

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

Buradaki blogumda çok fazla ayrıntı içeren bir yazı var


1

Bu yaklaşım, süreci canlı tutmak için herhangi bir kaynak tüketmeyecektir.

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

Yıkmak

  • while :; do sleep 1; done & Arka planda kukla bir süreç oluşturur
  • kill -STOP $! Arka plan işlemini durdurur
  • wait $! Arka plan sürecini bekleyin, bu sonsuza kadar bloke olacak, arka plan işlemi daha önce durduruldu

0

Pencere yöneticisini öldürmek yerine, yenisini --replaceveya -replacevarsa çalıştırmayı deneyin .


1
Eğer kullanırsam --replacehep böyle bir uyarı alırım another window manager is already running. Bu benim için pek mantıklı değil.
bekle

-2
while :; do read; done

çocuk uyku süreci için beklemek yok.


1
Bu stdinhala bağlı olması durumunda yiyor tty. Eğer < /dev/nullonu meşgul döngülerle çalıştırırsanız . Bazı durumlarda bazı yararlı olabilir, bu yüzden aşağı indirmem.
Tino

1
Bu çok kötü bir fikir, sadece cpu'nun her yerini tüketecek.
Mohammed Noureldin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.