Bir işlemi nasıl öldürebilirim ve PID'nin tekrar kullanılmadığından nasıl emin olabilirim?


40

Örneğin, aşağıdakine benzer bir kabuk komut dosyanız olduğunu varsayalım:

longrunningthing &
p=$!
echo Killing longrunningthing on PID $p in 24 hours
sleep 86400
echo Time up!
kill $p

Hile yapmalı, değil mi? İşlemin erken sona ermesi ve PID'sinin geri dönüştürülmesi dışında, bazı masum işlerin sinyal kuyruğunda bomba alması anlamına geliyor. Uygulamada bu muhtemelen önemli, ancak yine de beni endişelendiriyor. Tek başına ölmeyi bırakacak kadar uzun süren bir şeyi kesmek ya da FS'deki PID'sini saklamak / kaldırmak yapmak isterdi ama ben burada genel durumu düşünüyorum.


3
Hedefli süreciniz ölürse, sürecindeki katilini öldürdüğünden emin olun.
mikeserv

2
Adında killalleşleşenleri kullanın , en azından yalnızca aynı adda bir işlemi öldürüyorsunuz longrunningthing. Bir seferde bunlardan sadece birinin çalıştığını varsayalım.
LawrenceC

8
Orijinal işlemin başlangıç ​​zamanını kaydedebilir ve öldürmeden önce, işlemin başlangıç ​​işleminin kaydettiğiniz değerle eşleştiğini kontrol edebilirsiniz. Pid, start-time , Linux'taki işlemler için benzersiz bir tanımlayıcıdır.
Bakuriu

1
Buna neden ihtiyacın olduğunu sorabilir miyim ? ulaşmaya çalıştığınız temel şey nedir? (sürekli çalışan ancak her 24 saatte bir sıfırlanan bir şey var mı?)
Olivier Dulac

2
@mikeserv Bir süreç, kendi ölümünde bir şey olacağını garanti edemez.
kasperd

Yanıtlar:


29

En iyisi, timeoutkomutun bunun için olması durumunda kullanılmasıdır:

timeout 86400 cmd

Mevcut (8.23) GNU uygulaması en azından alarm()çocuk sürecini beklerken ya da eşdeğerini kullanarak çalışır . Geri dönüş ile çıkış SIGALRMarasında teslim edilmesine karşı koruma sağladığı görülmemektedir (bu alarmı etkin bir şekilde iptal etme ). Bu küçük pencere sırasında , stderr üzerine (örneğin çocuk bir göbek attıysa) bu yarış penceresini daha da büyütecek (örneğin stderr örneğin bir boru ise süresiz olarak) mesajlar yazabilir.waitpid()timeouttimeout

Şahsen ben bu sınırlama ile yaşayabilirim (ki bu muhtemelen gelecekteki bir versiyonda düzeltilecektir). timeoutayrıca doğru çıkış durumunu bildirmek, diğer köşe kasalarını (başlangıçta engellenen / yok sayılan SIGALRM gibi) ele almayı, muhtemelen elle yapmayı düşündüğünüzden daha iyi ele almaya da özen gösterir.

Bir yaklaşım olarak, şöyle yazabilirsiniz perl:

perl -MPOSIX -e '
  $p = fork();
  die "fork: $!\n" unless defined($p);
  if ($p) {
    $SIG{ALRM} = sub {
      kill "TERM", $p;
      exit 124;
    };
    alarm(86400);
    wait;
    exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
  } else {exec @ARGV}' cmd

Http://devel.ringlet.net/sysutils/timelimit/ adresinde bir timelimitkomut var. (GNU’yu birkaç ay öncesinden bekliyor ).timeout

 timelimit -t 86400 cmd

Biri alarm()benzeri bir mekanizma kullanır, ancak SIGCHLDölen çocuğun algılanması için bir işleyici (durdurulan çocukları görmezden gelir) kurar . Ayrıca, çalıştırmadan önce alarmı da iptal eder waitpid()( SIGALRMbeklemedeyse teslimatı iptal etmez , ancak yazıldığı şekilde, bir sorun olduğunu göremiyorum) ve aramadan önce öldürür waitpid()(bu nedenle yeniden kullanılan bir pid'i öldüremez) ).

netpipes'ın ayrıca bir timelimitkomutu var. Biri diğerlerini yıllarca ondan önce yutuyor, bir başka yaklaşımı daha alıyor, fakat durdurulan komutlar için düzgün çalışmıyor ve 1zaman aşımına uğradığında çıkış durumunu döndürüyor .

Sorunuza daha doğrudan bir cevap olarak, şöyle bir şey yapabilirsiniz:

if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
  kill "$p"
fi

Yani, sürecin hala bizim çocuğumuz olduğunu kontrol edin. Yine, ps(bu sürecin durumunu alma ve killöldürme arasında) sürecin ölebileceği ve ihalesinin başka bir işlem tarafından yeniden kullanılabileceği küçük bir yarış penceresi vardır .

Bazı kabukları ile ( zsh, bash, mksh), sen iş spesifikasyonları yerine pids geçebilir.

cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status

Bu, yalnızca bir arka plan işi oluşturduğunuzda işe yarar (aksi halde doğru iş hedefini almak her zaman güvenilir bir şekilde mümkün değildir).

Bu bir sorunsa, sadece yeni bir kabuk örneği başlatın:

bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd

Bu işe yarıyor, çünkü kabuk, çocuğun ölmesi üzerine işi iş masasından kaldırıyor. Burada, herhangi bir yarış penceresi olmamalıdır, çünkü kabuğun aradığı zaman, kill()SIGCHLD sinyali ele alınmamıştır ve iadenin yeniden kullanılması mümkün değildir (beklenmediğinden beri), ya da iş, işlem tablosundan kaldırıldı (ve killbir hata bildirildi). bash's killo genişletmek için kendi iş tablosunu erişmeden önce en az bloklar SIGCHLD az %sonra ve unblocks bunu kill().

Bu sleepişlemin cmdöldükten sonra da takılı kalmasını önlemek için başka bir seçenek , ile bashveya yerine ksh93bir boru kullanmaktır :read -tsleep

{
  {
    cmd 4>&1 >&3 3>&- &
    printf '%d\n.' "$!"
  } | {
    read p
    read -t 86400 || kill "$p"
  }
} 3>&1

Bu hala yarış koşullarına sahip ve sen komutun çıkış durumunu kaybedersin. Aynı zamanda cmdfd 4'ü kapatmayacağını da varsaymaktadır .

Yarışsız bir çözümü aşağıdaki perlgibi uygulamayı deneyebilirsiniz :

perl -MPOSIX -e '
   $p = fork();
   die "fork: $!\n" unless defined($p);
   if ($p) {
     $SIG{CHLD} = sub {
       $ss = POSIX::SigSet->new(SIGALRM); $oss = POSIX::SigSet->new;
       sigprocmask(SIG_BLOCK, $ss, $oss);
       waitpid($p,WNOHANG);
       exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
           unless $? == -1;
       sigprocmask(SIG_UNBLOCK, $oss);
     };
     $SIG{ALRM} = sub {
       kill "TERM", $p;
       exit 124;
     };
     alarm(86400);
     pause while 1;
   } else {exec @ARGV}' cmd args...

(başka tür köşe kasaları ile başa çıkmak için iyileştirilmesi gerekse de).

Yarışsız başka bir yöntem ise süreç gruplarını kullanıyor olabilir:

set -m
((sleep 86400; kill 0) & exec cmd)

Bununla birlikte, söz konusu terminal cihazına G / Ç olması durumunda, işlem gruplarını kullanmanın yan etkileri olabileceğini unutmayın. Üretildiği diğer tüm ekstra işlemleri öldürmekle birlikte, ek bir faydası vardır cmd.


4
Neden önce en iyi yöntemden bahsetmiyorsunuz?
15'de

2
@deltab: timeouttaşınabilir değil, yanıt ilk önce taşınabilir bir çözümden bahsetti.
cuonglm

1
@deltab: işlerin nasıl yürüdüğü ve özellikle “sağduyu” yaklaşımının nasıl başarısız olacağı hakkında fikir verir (Stephane, ilk önce balık tutmayı severim ki). Birinin tüm cevabı okuması bekleniyor
Olivier Dulac

@Stephane: "doğru iş hedefini her zaman güvenilir bir şekilde elde etmek mümkün değil" için: önce çıktısını sayamaz mısın jobsve sonra şunu öğrenemez misin (bir sonraki kabilinde olduğu gibi, kendi kabuğun olduğu gibi) iş N + 1 olacak? [o zaman N'yi kurtarabilir ve daha sonra% N + 1'i öldürebilir])
Olivier Dulac

1
@OlivierDulac, yeni bir işe başladığınızda hiçbir eski işin sona ermediğini varsaymayacak (iş numaralarının tekrar kullanılması).
Stéphane Chazelas

28

Genel olarak, yapamazsınız. Şimdiye kadar verilen cevapların hepsi buggy sezgiseldir. Pid'i sinyal göndermek için güvenle kullanabileceğiniz tek bir durum vardır: hedef süreç, sinyali gönderecek olan sürecin doğrudan bir çocuğu olduğunda ve ebeveyn henüz beklememiştir. Bu durumda, çıkmış olsa bile, ebeveyn ayrılana kadar (bu bir "zombi işlemi budur") pid ayrılmıştır. Bunu kabukla temiz bir şekilde yapmanın bir yolunun farkında değilim.

İşlemleri öldürmenin alternatif bir güvenli yolu, onları ana tarafın sahip olduğu sahte terminale ayarlanmış kontrol tty ile başlatmaktır. Ardından terminal üzerinden sinyal gönderebilirsiniz, örneğin karakterin üzerine SIGTERMveya SIGQUITüzerine yazma .

Komut dosyasıyla daha uygun bir başka yol, adlandırılmış bir screenoturumu kullanmak ve işlemi sona erdirmek için ekran oturumuna komutlar göndermektir. Bu işlem, güvenli bir benzersiz ad seçtiyseniz otomatik olarak yeniden kullanılmayacak olan, ekran oturumuna göre adlandırılan bir boru veya unix soketi üzerinde gerçekleşir.


4
Bunun neden kabuklarda yapılamadığını anlayamıyorum. Birkaç çözüm verdim.
Stéphane Chazelas

3
Lütfen bir açıklama ve yarış pencereleri ve diğer dezavantajlar hakkında bir miktar nicel tartışma yapabilir misiniz? Bu olmadan, “Şimdiye kadar verilen cevapların hepsi bir araya gelme sezgileridir” , sadece gereksiz yere bir faydası olmadan karşı karşıya.
Peterph

3
@peterph: Genel olarak, bir pid kullanımı herhangi bir TOCTOU yarışıdır - hala başvurmasını beklediğiniz sürecin aynı olup olmadığını nasıl kontrol ederseniz edin, o sürece atıfta bulunup yeni bir atıfta bulunmayı bırakabilir. kullanmadan önce aralıklarla işlem yapın (sinyal gönderilir). Bunu önlemenin tek yolu, ihalenin serbest / yeniden kullanımını engelleyebilmektir ve bunu yapabilen tek işlem doğrudan ebeveyndir.
R. ..

2
@ StéphaneChazelas: Kabuğun, çıkmış bir arka plan işleminin bir kısmını beklemesini nasıl önlersiniz? Bunu yapabiliyorsanız, OP'nin ihtiyacı olması durumunda sorun kolayca çözülebilir.
R. ..

5
@peterph: "Yarış penceresi küçük" bir çözüm değil. Ve yarışın nadirliği sıralı pid atamasına bağlıdır. Yılda bir kez olmak üzere çok kötü bir şeye neden olan hatalar, her zaman meydana gelen hatalardan çok daha kötüdür çünkü teşhis ve düzeltmek neredeyse imkansızdır.
R. ..

10
  1. Süreci başlatırken başlangıç ​​zamanından tasarruf edin:

    longrunningthing &
    p=$!
    stime=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    echo "Killing longrunningthing on PID $p in 24 hours"
    sleep 86400
    echo Time up!
    
  2. Süreci öldürmeyi denemeden önce durdurun (bu gerçekten gerekli değil, ancak yarış koşullarından kaçınmanın bir yolu: Süreci durdurursanız, pid tekrar kullanılamaz)

    kill -s STOP "$p"
    
  3. Bu PID ile işlemin aynı başlangıç ​​zamanına sahip olduğunu kontrol edin ve evet ise, onu öldürün, aksi takdirde işlemin devam etmesine izin verin:

    cur=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    if [ "$cur" = "$stime" ]
    then
        # Okay, we can kill that process
        kill "$p"
    else
        # PID was reused. Better unblock the process!
        echo "long running task already completed!"
        kill -s CONT "$p"
    fi
    

Bu işe yarar, çünkü aynı PID ile yalnızca bir işlem olabilir ve verilen bir işletim sisteminde başlangıç ​​zamanı olabilir.

Kontrol sırasında sürecin durdurulması, yarış koşullarını sorun yaratmaz. Belli ki bu, bazı milisaniyeler için bazı rasgele işlemlerin durdurulması sorununa sahiptir. İşlemin türüne bağlı olarak bu bir sorun olabilir veya olmayabilir.


Şahsen ben sadece python kullanırdım ve psutilhangisi PID'yi yeniden kullanır?

import time

import psutil

# note: it would be better if you were able to avoid using
#       shell=True here.
proc = psutil.Process('longrunningtask', shell=True)
time.sleep(86400)

# PID reuse handled by the library, no need to worry.
proc.terminate()   # or: proc.kill()

UNIX’de Python kuralları ... Neden daha fazla cevap başlamıyor emin değilim, çünkü çoğu sistemin kullanılmasını yasaklamadığından eminim.
Bay Mascaro

Daha önce benzer bir şema kullandım (başlangıç ​​zamanını kullandım), ama sh komut yazma becerilerin benimkilerden daha iyi! Teşekkürler.
FJL

Bu, potansiyel olarak yanlış süreci durdurduğunuz anlamına gelir. ps -o start=Bir süre sonra formatın 18: 12'den Jan26'ya değiştiğini unutmayın . DST değişikliklerinden de sakının. Eğer Linux'ta muhtemelen tercih edersiniz TZ=UTC0 ps -o lstart=.
Stéphane Chazelas

@ StéphaneChazelas Evet, ancak daha sonra devam etmesine izin veriyorsunuz. Açıkça söyledim: bu sürecin gerçekleştirdiği görevin türüne bağlı olarak, bir milisaniyeyi durdurmak için bazı sorunlar yaşayabilirsiniz. Tavsiyeniz için teşekkür ederiz. lstartBen düzenleyeceğim.
Bakuriu

(Sisteminiz kullanıcı başına işlem sayısını sınırlamadığı sürece), işlem masasını zombilerle doldurmak herkes için kolaydır. Geriye kalan sadece 3 pids kaldığında, herkesin aynı pid ile yüzlerce farklı işlemi bir saniyede başlatması kolaydır. Bu nedenle, kesinlikle söylemek gerekirse, “aynı PID ile yalnızca bir işlem olabilir ve verilen bir işletim sisteminde başlama zamanı” mutlaka doğru değildir.
Stéphane Chazelas

7

Linux sisteminde, bir pid'in pid ad alanını canlı tutarak tekrar kullanılmayacağından emin olabilirsiniz. Bu /proc/$pid/ns/piddosya aracılığıyla yapılabilir .

  • man namespaces -

    Bağlamayı (bkz mount(2)) bu dizindeki dosyalardan birinin dosya sistemindeki başka bir yere bağlanması, ad alanındaki şu anda tüm işlemler sona erse bile, pid tarafından belirtilen işlemin karşılık gelen ad alanını canlı tutar.

    Bu dizindeki dosyalardan birinin (veya bu dosyalardan birine bağlı bir dosya ) açılması , pid ile belirtilen işlemin karşılık gelen ad alanı için bir dosya tanıtıcısı döndürür. Bu dosya tanıtıcısı açık kaldığı sürece, ad alanındaki tüm işlemler sonlandırılsa bile ad alanı hayatta kalır. Dosya tanımlayıcısı için iletilebilir setns(2).

Bir grup süreci - temel olarak herhangi bir sayıda süreci - adlarını sıralayarak izole edebilirsiniz init.

  • man pid_namespaces -

    Yeni bir ad alanında oluşturulan ilk işlem (yani süreç kullanılarak oluşturulan clone(2) ile CLONE_NEWPID bayrak veya bir çağrı sonrasında bir işlemle oluşturulan ilk çocuk unshare(2)kullanılarak CLONE_NEWPID bayrağı) olan PID 1 ve olduğu initad işlemi ( bakınız init(1)) . Ad alanı içinde yetim kalan bir çocuk işlemi, init(1) (aynı PID ad alanındaki çocuğun atalarından birinin prctl(2) PR_SET_CHILD_SUBREAPER komutunu, kendisini yetimhanenin soyguncusu olarak işaretlemek için kullanması yerine) yerine, bu süreçle yeniden düzenlenecektir .

    initBir PID ad alanının işlemi sona ererse, çekirdek, ad alanındaki tüm işlemleri bir SIGKILL sinyali ile sonlandırır . Bu davranış, initişlemin bir PID ad alanının doğru çalışması için gerekli olduğu gerçeğini yansıtır .

util-linuxPaket ad alanlarını işlemek için birçok yararlı araçlar sağlar. Örneğin, unsharebir kullanıcı ad alanındaki haklarını henüz ayarlamadıysanız, süper kullanıcı hakları gerekir:

unshare -fp sh -c 'n=
    echo "PID = $$"
    until   [ "$((n+=1))" -gt 5 ]
    do      while   sleep 1
            do      date
            done    >>log 2>/dev/null   &
    done;   sleep 5' >log
cat log; sleep 2
echo 2 secs later...
tail -n1 log

Bir kullanıcı ad alanı için düzenleme yapmadıysanız, ayrıcalıkları hemen bırakarak isteğe bağlı komutları güvenle çalıştırabilirsiniz. runuserKomut başka bir şeydir (non setuid) tarafından sağlanan ikili util-linuxpaketi ve bunun gibi görünebilir birleşmeyle:

sudo unshare -fp runuser -u "$USER" -- sh -c '...'

...ve bunun gibi.

Yukarıdaki örnekte iki anahtarları geçirilen çağrılan yapar bayrağı ilk çocuk oluşturulan süreç ve garanti durumu ve talimat bayrak bir pid ad oluşturun.unshare(1)--forksh -cinit--pidunshare(1)

sh -cSüreç yumurtlar beş backgrounded çocuk kabukları - Her bir inifinite whileçıktısını eklemeye devam edecektir döngü datesonuna kadar loguzun olduğu kadar için sleep 1gerçek getiri. Bu süreçlerin yumurtlama sonra sharamaları sleepek 5 saniye boyunca o sonlandırır.

O takdirde belirterek belki değerinde -fbackgrounded bayrağı kullanılmamış hiçbiri whiledöngüler sonlandırmak, ama onunla ...

ÇIKTI:

PID = 1
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
2 secs later...
Mon Jan 26 19:17:48 PST 2015

Güçlü gibi görünen ilginç bir cevap. Muhtemelen temel kullanım için biraz overkill, ancak bir düşünce sahip olmaya değer.
Uriel

PID ad alanını canlı tutmanın PID'nin yeniden kullanımını nasıl veya neden önlediğini anlamıyorum. Alıntı yaptığınız manpage - Bu dosya tanımlayıcısı açık kaldığı sürece, ad alanı içindeki tüm işlemler sonlandırılsa bile ad alanı canlı kalır - işlemlerin hala sonlanabileceğini (ve böylece muhtemelen işlem kimliklerini geri dönüştürülmesini) önerir. PID ad alanını canlı tutmak, PID'nin kendisinin başka bir işlem tarafından tekrar kullanılmasını engellemekle ne ilgisi var?
davmac

5

longrunningthingBiraz daha iyi, biraz daha cini gibi davranmayı düşünün . Örneğin , en azından sürecin sınırlı bir şekilde kontrol edilmesine izin verecek bir pidfil dosyası oluşturabilirsiniz . Tamamı bir sarmalayıcı içeren orijinal ikili dosyayı değiştirmeden bunu yapmanın birkaç yolu vardır. Örneğin:

  1. gerekli işi arka planda başlatacak basit bir sarmalayıcı komut dosyası (isteğe bağlı çıktı yeniden yönlendirmesiyle), bu işlemin PID'sini bir dosyaya yazın, ardından işlemin bitmesini (kullanarak wait) bekleyin ve dosyayı kaldırın. Bekleme sırasında işlem, örneğin;

    kill $(cat pidfile)
    

    sarmalayıcı, sadece pid dosyasının kaldırıldığından emin olacaktır.

  2. kendi PID'ini bir yere koyacak ve kendisine gönderilen sinyalleri yakalayacak (ve buna cevap verecek) bir monitör ambalajı . Basit örnek:

    #!/bin/bash
    p=0
    trap killit USR1

    killit () {
        printf "USR1 caught, killing %s\n" "$p"
        kill -9 $p
    }

    printf "monitor $$ is waiting\n"
    therealstuff &
    p=%1
    wait $p
    printf "monitor exiting\n"

Şimdi, @R .. ve @ StéphaneChazelas'ın işaret ettiği gibi, bu yaklaşımlar sıklıkla bir yerde bir yarış koşuluna sahip olabilir veya doğurabileceğiniz işlemlerin sayısında bir sınırlama getirebilir. Buna ek olarak, longrunningthingmayıs çatalı ve çocukların ayrıldığı davaları ele almaz (bu, muhtemelen asıl soruda sorun neydi).

Son zamanlarda (birkaç yaşında okuyun) Linux çekirdeği ile, bazı modern Linux init sistemlerinin kullandığı alan olan dondurucu - gruplar kullanılarak , yani dondurucu ile güzelce muamele edilebilir .


Herkese teşekkürler. Şimdi her şeyi okuyorum. Asıl mesele longrunningthingne olduğu üzerinde kontrol sahibi olmamak . Bir kabuk betiği örneği de verdim, çünkü sorunu açıkladı. Sizinkini ve buradaki diğer yaratıcı çözümleri seviyorum, ancak Linux / bash kullanıyorsanız, bunun için yerleşik bir "zaman aşımı" var. Sanırım bu kaynağa ulaşmalı ve nasıl yapıldığını görmeliyim!
FJL

@FJL, timeoutbir değil, bir kabuk yerleşik. timeoutLinux için bir komutun çeşitli uygulamaları olmuştur , bunlardan biri yakın zamanda (2008) GNU coreutil'lerine (yani Linux'a özel değil) eklenmiştir ve bugünlerde çoğu Linux dağıtımının kullandığı şey budur.
Stéphane Chazelas

@ Stéphane - Teşekkürler - Ardından GNU coreutils'in referansını buldum. Taşınabilir olabilirler, ancak ana sistemde olmadığı sürece güvenilmez. Nasıl çalıştığını bilmekle daha fazla ilgileniyorum, ancak başka bir yerde yorumunuzu% 100 güvenilir olmadığını öne sürerek not ediyorum. Bu konunun gidiş yolu göz önüne alındığında, şaşırmadım!
FJL

1

Eğer Linux üzerinde çalışıyorsanız (ve bir kaç tane daha * nix), öldürmeyi düşündüğünüz işlemin hala kullanılıp kullanılmadığını ve komut satırının uzun işleminize uygun olup olmadığını kontrol edebilirsiniz . Gibi bir şey :

echo Time up!
grep -q longrunningthing /proc/$p/cmdline 2>/dev/null
if [ $? -eq 0 ]
then
  kill $p
fi

Bir alternatif, öldürmeyi planladığınız sürecin ne kadar sürdüğünü kontrol etmek olabilir ps -p $p -o etime=. Bu bilgiyi kendinizden ayıklayarak kendiniz yapabilirsiniz /proc/$p/stat, ancak bu çok zor olurdu (zaman jiffler cinsinden ölçülür ve sistemi de çalışma süresi içinde kullanmak zorunda kalırsınız /proc/stat).

Her neyse, genellikle çekinizden sonra ve öldürmeden önce işlemin değiştirilmediğinden emin olamazsınız .


Bu hala doğru değil çünkü yarış koşulundan kurtulmuyor.
strcat

Gerçekten de, Başarı garantisi yok, ancak çoğu komut dosyası böyle bir kontrol yapmak için zahmet etmiyor ve sadece açıkça bir cat pidfilesonucu öldürüyor . Sadece kabukta yapmanın temiz bir yolunu hatırlayamıyorum. Önerilen isim alanı cevabı ilginç görünüyor ancak ...
Uriel

-1

Bu aslında çok iyi bir soru.

İşlemin benzersizliğini belirleme yolu (a) hafızada olduğu yere bakmaktır; ve (b) hafızanın içerdiği şey. Daha açık olmak gerekirse, ilk çalıştırma için programın hafızasının nerede olduğunu bilmek istiyoruz, çünkü her bir iş parçasının metin alanının bellekte farklı bir konum işgal edeceğini biliyoruz. İşlem biterse ve bir başkası aynı ödemeyle başlatılırsa, yeni işlemin program metni hafızada aynı yerde kalmaz ve aynı bilgiyi içermez.

Yani, işleminizi başlattıktan hemen sonra, işlemi yapın md5sum /proc/[pid]/mapsve sonucu kaydedin. Daha sonra süreci öldürmek istediğinizde, başka bir md5sum yapın ve karşılaştırın. Eşleşirse, o zaman ihaleyi öldür. Eğer değilse, yapma.

Bunu kendiniz görmek için iki aynı bash kabuğunu açın. /proc/[pid]/mapsOnlar için inceleyin ve farklı olduklarını göreceksiniz. Neden? Çünkü aynı program olmasına rağmen, bellekte farklı yerler işgal ediyorlar ve yığınlarının adresleri farklı. Bu nedenle, işleminiz ölür ve PID'si yeniden kullanılırsa, aynı komut aynı argümanlar ile tekrar başlatılsa bile , "haritalar" dosyası farklı olacaktır ve orijinal işlemle ilgilenmediğinizi bileceksiniz.

Bakınız: detaylar için proc man sayfası .

Dosyanın /proc/[pid]/statdiğer afişlerin cevaplarında bahsettiği tüm bilgileri zaten içerdiğini unutmayın : işlemin yaşı, veli pid, vb. Bu dosya hem statik bilgi hem de dinamik bilgi içerir. Karşılaştırmaların ardından, başlattığınızda longrunningthing, aşağıdaki statik alanları statdosyadan çıkarmanız ve daha sonra karşılaştırmak üzere saklamanız gerekir:

pid, dosya adı, ebeveynin pid'i, işlem grubu kimliği, terminali denetleme, sistemin başlatılmasından sonra başlatılan zaman süreci, yerleşik küme büyüklüğü, yığının başlangıcının adresi,

Birlikte ele alındığında, yukarıdakiler süreci benzersiz bir şekilde tanımlamaktadır ve bu da bunun için başka bir yol temsil etmektedir. Aslında, "pid" den ve "sistem açılışından sonra başlayan zaman sürecinden" daha yüksek bir güven derecesiyle başlayamazsınız. Bu alanları statdosyadan çıkartın ve işleminizi başlattıktan sonra bir yere kaydedin. Öldürmeden önce daha sonra tekrar çıkarın ve karşılaştırın. Eşleşirlerse, orijinal işleme baktığınızdan emin olabilirsiniz.


1
Bu, genellikle /proc/[pid]/mapsfazladan bellek tahsis edildiğinde veya yığın büyüdüğünde veya yeni dosyalar eşleştikçe zamanla değişiklik olarak işe yaramaz ... Ve lansmandan hemen sonra ne anlama geliyor? Bütün kütüphaneler eşleştirildikten sonra mı? Bunu nasıl belirliyorsunuz?
Stéphane Chazelas

Şimdi sistemimde iki işlem, biri java uygulaması ve diğeri cfengine sunucusu ile bir test yapıyorum. Her 15 dakikada bir md5sumkendi harita dosyalarında yapıyorum . Bir veya iki gün boyunca çalışmasına izin vereceğim ve sonuçlarla burada rapor edeceğim.
Michael Martinez,

@ StéphaneChazelas: İki işlemimi 16 saatten beri kontrol ediyorum ve md5sum'da bir değişiklik olmadı
Michael Martinez

-1

Başka bir yol, öldürmeden önce sürecin yaşını kontrol etmektir. Bu şekilde, 24 saatten daha az bir sürede doğmayan bir süreci öldürmediğinizden emin olabilirsiniz. ifSüreci öldürmeden önce buna göre bir koşul ekleyebilirsiniz .

if [[ $(ps -p $p -o etime=) =~ 1-. ]] ; then
    kill $p
fi

Bu ifkoşul, işlem kimliğinin $p24 saatten az olup olmadığını kontrol edecektir (86400 saniye).

Not: - Komut ps -p $p -o etime=formatta olacak<no.of days>-HH:MM:SS


mtimeAit /proc/$psürecinin başlangıç zamanı olan ilgisi yoktur.
Stéphane Chazelas

Teşekkürler @ StéphaneChazelas. Haklısın. ifDurumu değiştirmek için cevabı değiştirdim . Buggy ise yorum yapmaktan çekinmeyin.
Sree

-3

Yaptığım şey, süreci öldürdükten sonra tekrar yapmak. Bunu her yaptığımda cevap geri dönüyor, "böyle bir süreç yok"

allenb   12084  5473  0 08:12 pts/4    00:00:00 man man
allenb@allenb-P7812 ~ $ kill -9 12084
allenb@allenb-P7812 ~ $ kill -9 12084
bash: kill: (12084) - No such process
allenb@allenb-P7812 ~ $ 

Daha basit olamazdı ve bunu yıllardır problemsiz yapıyorum.


Bu, "nasıl düzeltebilirim" sorusuna değil, "nasıl daha kötü hale getirebilirim" sorusuna cevap veriyor.
Stéphane Chazelas,
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.