Bagaimana saya bisa mematikan proses dan memastikan PID belum digunakan kembali

40

Misalkan, misalnya, Anda memiliki skrip shell yang mirip dengan:

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

Harus melakukan trik, bukan? Kecuali bahwa proses tersebut mungkin telah dihentikan lebih awal dan PID-nya mungkin telah didaur ulang, artinya beberapa pekerjaan yang tidak bersalah mendapatkan bom dalam antrian sinyalnya. Dalam praktiknya ini mungkin memang penting, tetapi tetap saja itu mengkhawatirkan saya. Meretas longrunning untuk mati dengan sendirinya, atau mempertahankan / menghapus PID-nya di FS akan dilakukan, tetapi saya sedang memikirkan situasi umum di sini.

FJL
sumber
3
Anda memastikan bahwa jika proses yang ditargetkan mati, itu membunuh pembunuhnya dalam proses.
mikeserv
2
Gunakan killallyang cocok dengan nama, jadi setidaknya Anda hanya membunuh proses dengan nama yang sama longrunningthing. Dengan asumsi Anda hanya akan menjalankan salah satu dari ini pada satu waktu.
LawrenceC
8
Anda dapat menyimpan waktu mulai dari proses asli dan, sebelum membunuh, periksa bahwa waktu mulai proses dengan pid itu sesuai dengan apa yang Anda simpan. Pasangan pid, waktu mulai adalah pengidentifikasi unik untuk proses di Linux.
Bakuriu
1
Bolehkah saya bertanya mengapa Anda membutuhkan ini? apa hal mendasar yang ingin Anda capai? (sesuatu yang berjalan terus-menerus tetapi diatur ulang setiap 24 jam?)
Olivier Dulac
2
@ mikeserv Suatu proses tidak dapat menjamin bahwa sesuatu akan terjadi jika terjadi kematiannya sendiri.
kasperd

Jawaban:

29

Yang terbaik adalah menggunakan timeoutperintah jika Anda memilikinya yang dimaksudkan untuk itu:

timeout 86400 cmd

Implementasi GNU saat ini (8,23) setidaknya bekerja dengan menggunakan alarm()atau setara saat menunggu proses anak. Tampaknya tidak menjaga terhadap SIGALRMyang disampaikan di antara waitpid()kembali dan timeoutkeluar (secara efektif membatalkan alarm itu ). Selama jendela kecil itu,timeout bahkan mungkin menulis pesan di stderr (misalnya jika anak membuang inti) yang akan lebih memperbesar jendela ras itu (tanpa batas jika stderr adalah pipa penuh misalnya).

Saya pribadi dapat hidup dengan batasan itu (yang mungkin akan diperbaiki dalam versi yang akan datang). timeoutjuga akan sangat berhati-hati untuk melaporkan status keluar yang benar, menangani kasus sudut lainnya (seperti SIGALRM diblokir / diabaikan pada saat startup, menangani sinyal lain ...) lebih baik daripada yang mungkin Anda bisa lakukan dengan tangan.

Sebagai perkiraan, Anda bisa menulis perlseperti:

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

Ada timelimitperintah di http://devel.ringlet.net/sysutils/timelimit/ (mendahului GNU timeoutbeberapa bulan).

 timelimit -t 86400 cmd

Yang itu menggunakan alarm()mekanisme mirip-tetapi menginstal pawang pada SIGCHLD(mengabaikan anak-anak yang berhenti) untuk mendeteksi anak sekarat. Itu juga membatalkan alarm sebelum menjalankan waitpid()(itu tidak membatalkan pengiriman SIGALRMjika itu tertunda, tetapi cara itu ditulis, saya tidak bisa melihatnya menjadi masalah) dan membunuh sebelum memanggil waitpid()(jadi tidak bisa membunuh pid digunakan kembali ).

netpipes juga memiliki timelimitperintah. Yang satu mendahului semua yang lain dalam beberapa dekade, membutuhkan pendekatan lain, tetapi tidak berfungsi dengan benar untuk perintah yang berhenti dan mengembalikan a1 status keluar setelah batas waktu.

Sebagai jawaban yang lebih langsung untuk pertanyaan Anda, Anda dapat melakukan sesuatu seperti:

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

Yaitu, periksa apakah prosesnya masih anak-anak kita. Sekali lagi, ada jendela ras kecil (di antara psmengambil status proses itu dan killmembunuhnya) selama proses itu bisa mati dan pid nya digunakan kembali oleh proses lain.

Dengan beberapa kerang ( zsh, bash, mksh), Anda dapat melewati spesifikasi pekerjaan bukan PID.

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

Itu hanya bekerja jika Anda menelurkan hanya satu pekerjaan latar belakang (jika tidak mendapatkan jobspec yang tepat tidak selalu mungkin andal).

Jika itu masalah, mulai saja instance shell baru:

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

Itu bekerja karena shell menghapus pekerjaan dari tabel pekerjaan pada saat anak sekarat. Di sini, seharusnya tidak ada jendela balapan karena pada saat shell memanggil kill(), baik sinyal SIGCHLD belum ditangani dan pid tidak dapat digunakan kembali (karena belum menunggu), atau sudah ditangani dan pekerjaan telah dihapus dari tabel proses (dan killakan melaporkan kesalahan). bashIni killsetidaknya blok SIGCHLD sebelum mengakses meja tugasnya untuk memperluas %dan unblocks itu setelah kill().

Pilihan lain untuk menghindari sleepproses yang berkeliaran bahkan setelah cmdtelah mati, dengan bashatau ksh93menggunakan pipa dengan read -tbukannya sleep:

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

Yang itu masih memiliki kondisi balapan, dan Anda kehilangan status keluar perintah. Ia juga menganggap cmdtidak menutup fd 4.

Anda dapat mencoba menerapkan solusi bebas ras di perlseperti:

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...

(Meskipun itu perlu ditingkatkan untuk menangani jenis kasus sudut lainnya).

Metode bebas ras lain bisa menggunakan grup proses:

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

Namun perhatikan bahwa menggunakan grup proses dapat memiliki efek samping jika ada I / O ke perangkat terminal yang terlibat. Ini memiliki manfaat tambahan meskipun untuk membunuh semua proses ekstra lainnya yang dihasilkan oleh cmd.

Stéphane Chazelas
sumber
4
Mengapa tidak menyebutkan metode terbaik dulu?
deltab
2
@deltab: timeouttidak portabel, jawabannya menyebutkan solusi portabel terlebih dahulu.
cuonglm
1
@deltab: ini memberikan wawasan tentang bagaimana segala sesuatunya bekerja dan terutama bagaimana pendekatan "akal sehat" dapat gagal (Stephane lebih suka mengajar seseorang untuk memancing terlebih dahulu, yang saya suka). Seseorang diharapkan membaca seluruh jawaban
Olivier Dulac
@Stephane: untuk "mendapatkan jobspec yang tepat tidak selalu mungkin andal": tidak bisakah Anda pertama-tama menghitung output jobsdan kemudian mengetahui bahwa (karena itu adalah shell Anda sendiri, di mana Anda memiliki kendali atas apa yang terjadi selanjutnya) yang berikutnya dilatar belakangi pekerjaan akan menjadi N +1? [maka Anda dapat menyimpan N, dan kemudian membunuh% N +1])
Olivier Dulac
1
@OlivierDulac, yang akan menganggap tidak ada pekerjaan sebelumnya yang dihentikan pada saat Anda memulai yang baru (shells menggunakan kembali nomor pekerjaan).
Stéphane Chazelas
28

Secara umum, Anda tidak bisa. Semua jawaban yang diberikan sejauh ini adalah heuristik bermasalah. Hanya ada satu kasus di mana Anda dapat dengan aman menggunakan pid untuk mengirim sinyal: ketika proses target adalah anak langsung dari proses yang akan mengirimkan sinyal, dan orang tua belum menunggu di atasnya. Dalam hal ini, bahkan jika sudah keluar, pid dicadangkan (inilah "proses zombie") sampai orang tua menunggu di atasnya. Saya tidak mengetahui cara untuk melakukan itu dengan bersih dengan shell.

Cara aman alternatif untuk mematikan proses adalah memulainya dengan tty pengendali yang disetel ke terminal semu yang Anda miliki dari sisi master. Anda kemudian dapat mengirim sinyal melalui terminal, misalnya menulis karakter untuk SIGTERMatauSIGQUIT lebih dari pty.

Namun cara lain yang lebih nyaman dengan scripting adalah dengan menggunakan screensesi bernama dan mengirim perintah ke sesi layar untuk mengakhirinya. Proses ini berlangsung di atas pipa atau soket unix bernama sesuai dengan sesi layar, yang tidak akan secara otomatis digunakan kembali jika Anda memilih nama unik yang aman.

R ..
sumber
4
Saya tidak bisa melihat mengapa itu tidak bisa dilakukan dalam kerang. Saya sudah memberikan beberapa solusi.
Stéphane Chazelas
3
Bisakah Anda memberikan penjelasan dan semacam diskusi kuantitatif tentang perlombaan dan kelemahan lainnya? Tanpa itu, "Semua jawaban yang diberikan sejauh ini adalah heuristik kereta" adalah konfrontasi yang tidak perlu tanpa manfaat.
peterph
3
@peterph: Secara umum, setiap penggunaan pid adalah perlombaan TOCTOU - tidak peduli bagaimana Anda memeriksa apakah masih merujuk ke proses yang sama dengan yang Anda harapkan, itu bisa berhenti merujuk pada proses itu dan merujuk ke yang baru proses dalam interval sebelum Anda menggunakannya (mengirim sinyal). Satu-satunya cara untuk mencegah hal ini adalah dengan dapat memblokir membebaskan / menggunakan kembali pid, dan satu-satunya proses yang dapat melakukan ini adalah orang tua langsung.
R ..
2
@ StéphaneChazelas: Bagaimana Anda mencegah shell menunggu pid dari proses latar belakang yang telah keluar? Jika Anda bisa melakukan itu, masalahnya mudah dipecahkan dalam hal kebutuhan OP.
R ..
5
@peterph: "Jendela perlombaan kecil" bukanlah solusi. Dan kelangkaan balapan tergantung pada tugas pid berurutan. Bug yang menyebabkan sesuatu yang sangat buruk terjadi sekali dalam setahun jauh lebih buruk daripada bug yang terjadi setiap saat karena mereka hampir mustahil untuk didiagnosis dan diperbaiki.
R ..
10
  1. Saat meluncurkan proses, hemat waktu mulainya:

    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. Sebelum mencoba untuk menghentikan proses hentikan itu (ini tidak benar-benar penting, tetapi ini adalah cara untuk menghindari kondisi balapan: jika Anda menghentikan proses, itu pid tidak dapat digunakan kembali)

    kill -s STOP "$p"
    
  3. Periksa bahwa proses dengan PID itu memiliki waktu mulai yang sama dan jika ya, bunuh, jika tidak biarkan proses berlanjut:

    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
    

Ini berfungsi karena hanya ada satu proses dengan PID yang sama dan waktu mulai pada OS yang diberikan.

Menghentikan proses selama pemeriksaan membuat kondisi ras tidak menjadi masalah. Jelas ini memiliki masalah itu, beberapa proses acak dapat dihentikan selama beberapa milidetik. Bergantung pada jenis proses ini mungkin atau mungkin tidak menjadi masalah.


Secara pribadi saya cukup menggunakan python dan psutilyang menangani penggunaan kembali PID secara otomatis:

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()
Bakuriu
sumber
Aturan Python di UNIX ... Saya tidak yakin mengapa lebih banyak jawaban tidak dimulai di sana karena saya yakin sebagian besar sistem tidak melarang penggunaannya.
Tn. Mascaro
Saya telah menggunakan skema serupa (menggunakan waktu mulai) sebelumnya, tetapi keterampilan skrip sh Anda lebih rapi daripada saya! Terima kasih.
FJL
Itu berarti Anda berpotensi menghentikan proses yang salah. Perhatikan bahwa ps -o start=format berubah dari 18:12 menjadi Jan26 setelah beberapa saat. Waspadalah terhadap perubahan DST juga. Jika di Linux, Anda mungkin lebih suka TZ=UTC0 ps -o lstart=.
Stéphane Chazelas
@ StéphaneChazelas Ya, tetapi Anda membiarkannya berlanjut setelahnya. Saya secara eksplisit mengatakan: tergantung pada jenis tugas yang proses itu lakukan, Anda mungkin mengalami kesulitan untuk menghentikannya beberapa milidetik. Terima kasih atas tipnya lstart, saya akan mengeditnya.
Bakuriu
Perhatikan bahwa (kecuali sistem Anda membatasi jumlah proses per pengguna), mudah bagi siapa saja untuk mengisi tabel proses dengan zombie. Setelah hanya ada 3 pids yang tersedia, mudah bagi siapa saja untuk memulai ratusan proses berbeda dengan pid yang sama dalam satu detik. Jadi, sebenarnya, "hanya ada satu proses dengan PID yang sama dan waktu mulai pada OS yang diberikan" belum tentu benar.
Stéphane Chazelas
7

Pada sistem linux, Anda dapat memastikan bahwa pid tidak akan digunakan kembali dengan menjaga namespace pid-nya tetap hidup. Ini dapat dilakukan melalui /proc/$pid/ns/pidfile.

  • man namespaces -

    Bind mounting (lihat mount(2)) salah satu file di direktori ini ke tempat lain di filesystem menjaga namespace yang sesuai dari proses yang ditentukan oleh pid hidup bahkan jika semua proses saat ini dalam namespace berakhir.

    Membuka salah satu file di direktori ini (atau file yang diikat di-mount ke salah satu file ini) mengembalikan file menangani untuk namespace yang sesuai dari proses yang ditentukan oleh pid. Selama deskriptor file ini tetap terbuka, namespace akan tetap hidup, bahkan jika semua proses dalam namespace berakhir. Deskriptor file dapat diteruskan ke setns(2).

Anda dapat mengisolasi sekelompok proses - pada dasarnya sejumlah proses - dengan menamai prosesnya init.

  • man pid_namespaces -

    Proses pertama dibuat dalam namespace baru (yaitu, proses dibuat menggunakan clone(2) dengan CLONE_NEWPID bendera, atau anak pertama diciptakan oleh proses setelah panggilan untuk unshare(2)menggunakan CLONE_NEWPID bendera) telah dengan PID 1 , dan merupakan initproses untuk namespace ( lihat init(1)) . Proses anak yang menjadi yatim di dalam namespace akan direparasi ke proses ini alih-alih init(1) (kecuali salah satu leluhur anak dalam namespace PID yang sama menggunakan perintah prctl(2) PR_SET_CHILD_SUBREAPER untuk menandai dirinya sebagai penuai kembali proses turunan anak yatim) .

    Jika initproses namespace PID berakhir, kernel mengakhiri semua proses dalam namespace melalui sinyal SIGKILL . Perilaku ini mencerminkan fakta bahwa initproses ini penting untuk operasi yang benar dari namespace PID .

The util-linuxpaket menyediakan banyak alat yang berguna untuk memanipulasi ruang nama. Misalnya, ada unshare, jika Anda belum mengatur haknya di ruang nama pengguna, itu akan membutuhkan hak pengguna super:

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

Jika Anda belum mengatur namespace pengguna, maka Anda masih dapat dengan aman menjalankan perintah arbitrer dengan segera menjatuhkan hak istimewa. The runuserperintah lain (non setuid) biner yang disediakan oleh util-linuxpaket dan menggabungkan mungkin terlihat seperti:

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

...dan seterusnya.

Dalam contoh di atas dua switch dilewatkan ke unshare(1)dalam --forkbendera yang membuat dipanggil sh -cproses anak pertama dibuat dan memastikan nya initstatus, dan --pidbendera yang menginstruksikanunshare(1) untuk membuat namespace pid.

The sh -cProses menumbuhkan lima kerang anak dilatarbelakangi - masing sebuah inifinite whileloop yang akan terus menambahkan output dari dateke ujung logselama sleep 1pengembalian benar. Setelah menelurkan proses ini shpanggilansleep selama 5 detik tambahan kemudian berakhir.

Mungkin perlu dicatat bahwa jika -fbendera tidak digunakan, tidak ada yang berlatar belakangwhile loop akan berakhir, tetapi dengan itu ...

KELUARAN:

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
mikeserv
sumber
Jawaban menarik yang tampaknya kuat. Mungkin sedikit berlebihan untuk penggunaan dasar, tetapi patut dipikirkan.
Uriel
Saya tidak melihat bagaimana atau mengapa menjaga namespace PID tetap hidup mencegah penggunaan kembali PID. Halaman yang Anda kutip - Selama deskriptor file ini tetap terbuka, namespace akan tetap hidup, bahkan jika semua proses dalam namespace berakhir - menunjukkan bahwa proses tersebut masih dapat dihentikan (dan dengan demikian mungkin ID prosesnya didaur ulang). Apa yang membuat menjaga namespace PID tetap hidup dengan mencegah PID itu sendiri digunakan kembali oleh proses lain?
davmac
5

Pertimbangkan untuk membuat Anda longrunningthingberperilaku sedikit lebih baik, sedikit lebih seperti daemon. Misalnya, Anda dapat membuatnya membuat pidfile yang akan memungkinkan setidaknya beberapa kontrol terbatas dari proses. Ada beberapa cara untuk melakukan ini tanpa memodifikasi biner asli, semuanya melibatkan pembungkus. Sebagai contoh:

  1. skrip pembungkus sederhana yang akan memulai pekerjaan yang diperlukan di latar belakang (dengan redirection output opsional), tulis PID dari proses ini ke dalam file, kemudian tunggu proses untuk menyelesaikan (menggunakan wait) dan menghapus file. Jika selama menunggu proses akan terbunuh misalnya oleh sesuatu seperti

    kill $(cat pidfile)
    

    bungkusnya hanya akan memastikan pidfile dihapus.

  2. bungkus monitor, yang akan menempatkan PID -nya sendiri di suatu tempat dan menangkap (dan merespons) sinyal yang dikirim kepadanya. Contoh sederhana:

    #!/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"

Sekarang, seperti yang ditunjukkan oleh @R .. dan @ StéphaneChazelas, pendekatan ini sering memiliki kondisi balapan di suatu tempat atau memaksakan batasan pada jumlah proses yang dapat Anda hasilkan. Selain itu, tidak menangani kasus, di manalongrunningthing garpu dan anak-anak bisa terlepas (yang mungkin bukan apa masalah dalam pertanyaan awal).

Dengan kernel Linux baru-baru ini (baca beberapa tahun), ini dapat diperlakukan dengan baik dengan menggunakan cgroup , yaitu freezer - yang, saya kira, adalah apa yang digunakan oleh beberapa sistem init Linux modern.

peterph
sumber
Terima kasih, dan untuk semuanya. Saya membaca semuanya sekarang. Intinya longrunningthingadalah bahwa Anda tidak memiliki kendali atas apa itu. Saya juga memberikan contoh skrip shell karena menjelaskan masalahnya. Saya suka milik Anda, dan semua solusi kreatif lainnya di sini, tetapi jika Anda menggunakan Linux / bash, ada batas waktu "built-in" untuk itu. Saya kira saya harus mendapatkan sumber itu dan melihat bagaimana melakukannya!
FJL
@FJL, timeoutadalah tidak builtin shell. Ada berbagai implementasi timeoutperintah untuk Linux, satu baru-baru ini (2008) ditambahkan ke GNU coreutils (jadi tidak spesifik untuk Linux), dan itulah yang sebagian besar distribusi Linux gunakan saat ini.
Stéphane Chazelas
@ Stéphane - Terima kasih - Saya kemudian menemukan referensi ke GNU coreutils. Mereka mungkin portabel, tetapi kecuali dalam sistem basis tidak dapat diandalkan. Saya lebih tertarik mengetahui cara kerjanya, meskipun saya perhatikan komentar Anda di tempat lain yang menyatakan itu tidak dapat diandalkan 100%. Mengingat cara utas ini berjalan, saya tidak terkejut!
FJL
1

Jika Anda menjalankan Linux (dan beberapa * nixes lainnya), Anda dapat memeriksa apakah proses yang ingin Anda bunuh masih digunakan dan bahwa baris perintah cocok dengan proses panjang Anda. Sesuatu seperti :

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

Alternatifnya adalah memeriksa berapa lama proses yang ingin Anda bunuh berjalan, dengan sesuatu seperti ps -p $p -o etime=. Anda dapat melakukannya sendiri dengan mengekstraksi informasi ini dari /proc/$p/stat, tetapi ini akan sulit (waktu diukur dalam jiffies, dan Anda harus menggunakan waktu kerja sistem dalam/proc/stat juga).

Bagaimanapun, Anda biasanya tidak dapat memastikan bahwa proses tersebut tidak diganti setelah cek dan sebelum Anda membunuhnya.

Uriel
sumber
Itu masih tidak benar karena tidak menyingkirkan kondisi balapan.
strcat
@strcat Memang, Tidak ada jaminan sukses, tetapi sebagian besar skrip bahkan tidak repot-repot melakukan pemeriksaan seperti itu dan hanya membunuh cat pidfilehasilnya secara blak-blakan . Saya tidak ingat cara bersih untuk melakukannya di shell saja. Namun, jawaban namespace yang diusulkan tampaknya menarik ...
Uriel
-1

Ini sebenarnya pertanyaan yang sangat bagus.

Cara untuk menentukan keunikan proses adalah dengan melihat (a) di mana ia berada dalam memori; dan (b) apa isi memori itu. Untuk lebih spesifik, kami ingin tahu di mana dalam memori adalah teks program untuk doa awal, karena kami tahu bahwa area teks dari setiap utas akan menempati lokasi yang berbeda dalam memori. Jika proses mati dan yang lain diluncurkan dengan pid yang sama, teks program untuk proses baru tidak akan menempati tempat yang sama di memori dan tidak akan berisi informasi yang sama.

Jadi, segera setelah meluncurkan proses Anda, lakukan md5sum /proc/[pid]/mapsdan simpan hasilnya. Kemudian ketika Anda ingin mematikan proses, lakukan md5sum lain dan bandingkan. Jika cocok, maka bunuh pid itu. Jika tidak, jangan.

untuk melihat ini sendiri, luncurkan dua shell bash yang identik. Periksa /proc/[pid]/mapsuntuk mereka dan Anda akan menemukan bahwa mereka berbeda. Mengapa? Karena meskipun itu adalah program yang sama, mereka menempati lokasi yang berbeda dalam memori dan alamat tumpukan mereka berbeda. Jadi, jika proses Anda mati dan PID digunakan kembali, bahkan dengan perintah yang sama diluncurkan kembali dengan argumen yang sama , file "peta" akan berbeda dan Anda akan tahu bahwa Anda tidak berurusan dengan proses asli.

Lihat: halaman manual proc untuk detailnya.

Perhatikan bahwa file tersebut /proc/[pid]/statsudah berisi semua informasi yang disebutkan oleh poster lain dalam jawaban mereka: usia proses, orang tua pid, dll. File ini berisi informasi statis dan informasi dinamis, jadi jika Anda lebih suka menggunakan file ini sebagai basis perbandingan, lalu saat meluncurkan longrunningthing, Anda perlu mengekstrak bidang statis berikut dari statfile dan menyimpannya untuk perbandingan nanti:

pid, nama file, pid orang tua, id grup proses, terminal pengendali, waktu proses dimulai setelah boot sistem, ukuran set penduduk, alamat mulai stack,

secara bersama-sama, proses di atas secara unik mengidentifikasi proses, dan karenanya ini merupakan cara lain untuk melangkah. Sebenarnya Anda bisa lolos dengan tidak lebih dari "pid" dan "proses waktu dimulai setelah boot sistem" dengan tingkat kepercayaan yang tinggi. Cukup ekstrak bidang ini dari statfile dan simpan di suatu tempat setelah meluncurkan proses Anda. Kemudian sebelum membunuhnya, ekstrak lagi dan bandingkan. Jika mereka cocok, maka Anda yakin Anda sedang melihat proses aslinya.

Michael Martinez
sumber
1
Itu umumnya tidak akan berfungsi sebagai /proc/[pid]/mapsperubahan dari waktu ke waktu sebagai alokasi memori tambahan atau tumpukan tumbuh atau file baru mmapped ... Dan apa artinya segera setelah peluncuran berarti? Setelah semua perpustakaan di-mmapped? Bagaimana cara anda menentukan itu?
Stéphane Chazelas
Saya sedang melakukan tes sekarang di sistem saya dengan dua proses, satu aplikasi java dan yang lain server cfengine. Setiap 15 menit saya lakukan md5sumpada file peta mereka. Saya akan membiarkannya berjalan selama satu atau dua hari dan melaporkan kembali ke sini dengan hasilnya.
Michael Martinez
@ StéphaneChazelas: Saya telah memeriksa dua proses saya selama 16 jam sekarang, dan tidak ada perubahan pada md5sum
Michael Martinez
-1

Cara lain adalah memeriksa usia proses sebelum membunuhnya. Dengan begitu, Anda dapat memastikan Anda tidak membunuh proses yang tidak muncul dalam waktu kurang dari 24 jam. Anda dapat menambahkan ifkondisi berdasarkan itu sebelum mematikan proses.

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

ifKondisi ini akan memeriksa apakah ID proses $pkurang dari 24 jam (86400 detik).

PS: - Perintah ps -p $p -o etime=akan memiliki format<no.of days>-HH:MM:SS

Sree
sumber
The mtimedari /proc/$ptidak ada hubungannya dengan waktu mulai dari proses.
Stéphane Chazelas
Terima kasih @ StéphaneChazelas. Kamu benar. Saya telah mengedit jawaban untuk mengubah ifkondisi. Jangan ragu untuk berkomentar jika itu bermasalah.
Sree
-3

Apa yang saya lakukan adalah, setelah membunuh prosesnya, lakukan lagi. Setiap kali saya melakukannya, jawabannya kembali, "tidak ada proses seperti itu"

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 ~ $ 

Tidak bisa lebih sederhana dan saya sudah melakukan ini selama bertahun-tahun tanpa masalah.

Allen
sumber
Itu menjawab pertanyaan "bagaimana saya bisa membuatnya lebih buruk", bukan "bagaimana saya bisa memperbaikinya".
Stéphane Chazelas