Mengapa SIGKILL tidak menghentikan program yang berhenti (ya)?

8

Saya menggunakan Ubuntu 14.04 dan saya mengalami perilaku ini yang sepertinya tidak saya mengerti:

  1. Jalankan yesperintah (dalam shell default: Bash )
  2. Ketik CtrlZuntuk berhentiyes
  3. Lari jobs. Keluaran:
    [1]+ Stopped yes
  4. Lari kill -9 %1untuk berhenti yes. Keluaran:
    [1]+ Stopped yes
  5. Lari jobs. Keluaran:
    [1]+ Stopped yes

Ini ada di Ubuntu yang 3.16.0-30-genericberjalan di mesin virtual paralel.

Mengapa kill -9perintah saya tidak menghentikan perintah ya ? Saya pikir SIGKILL tidak dapat ditangkap atau diabaikan? Dan bagaimana saya bisa menghentikan perintah yes ?

s1m0n
sumber
1
Itu menarik. SIGKILL harus bekerja dan berfungsi pada Linux Mint saya 17. Untuk sinyal lain, Anda biasanya perlu mengirimkannya SIGCONT sesudahnya untuk memastikan sinyal diterima oleh target yang berhenti.
PSkocik
Apakah bash benar-benar mencetak "Berhenti" untuk proses yang ditangguhkan ?
edmz
Versi kernel ( uname -a) tolong
roaima
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux. Saya menjalankan Ubuntu di Parallels Desktop.
s1m0n
1
@black sebagian besar kerang mengatakan "Berhenti". tcsh mengatakan "Ditangguhkan" dan zsh mengatakan "ditangguhkan". Perbedaan kosmetik. Yang agak lebih penting adalah fakta bahwa bash mencetak pesan yang identik untuk STOP dan TSTP, di mana semua shell lain menandai anotasi pesan STOP dengan (signal)sehingga Anda dapat membedakannya.

Jawaban:

10

Sinyal diblokir untuk proses yang ditangguhkan. Di terminal:

$ yes
...
y
y
^Zy

[1]+  Stopped                 yes

Di terminal kedua:

$ killall yes

Di terminal pertama:

$ jobs
[1]+  Stopped                 yes

$ fg
yes
Terminated

Namun SIGKILLtidak dapat diblokir. Melakukan hal yang sama dengan killall -9 yesdari terminal kedua segera memberikan ini di yesterminal:

[1]+  Killed                  yes

Akibatnya jika kill -9 %1tidak menghentikan proses segera maka bashtidak benar-benar mengirim sinyal sampai Anda fgproses, atau Anda telah menemukan bug di kernel.

lcd047
sumber
4
Beberapa detail latar belakang: Ketika mengeluarkan Ctrl + Z di bash terminal Anda mengirimkan SIGTSTP(yang merupakan versi yang dapat diblokir SIGSTOP) ke proses aktif. Ini menempatkan proses dalam keadaan beku di mana kernel tidak akan menjadwalkannya. Itu juga menghambat pemrosesan sinyal (kecuali untuk SIGCONTsinyal yang tidak membekukan proses) dan karenanya mencegah proses agar tidak segera dimatikan.
mreithub
1
SIGKILL, tidak seperti sinyal lain, tidak diblokir untuk proses yang ditangguhkan. Mengirim sinyal KILL ke proses yang ditangguhkan akan membunuhnya - secara asinkron, tetapi dalam praktiknya pada dasarnya segera.
Gilles 'SANGAT berhenti menjadi jahat'
1
@Gilles Itulah yang saya coba ilustrasikan di atas: SIGTERMdiblokir, tetapi SIGKILLtidak. Bagaimanapun, menurut komentar dari OP, masalahnya tampaknya jobstidak mendeteksi bahwa proses telah mati, bukan proses yang tidak dibunuh kill -9 %1.
lcd047
1
Tapi saya bisa mereproduksi perilaku s1m0n di sistem saya (Debian, amd64, bash 4.3.30).
Gilles 'SANGAT berhenti menjadi jahat'
1
Meskipun SIGKILLtidak dapat diblokir, tidak ada jaminan bahwa itu akan dikirimkan dalam waktu yang berarti. Jika suatu proses ditangguhkan sambil menunggu pemblokiran I / O, misalnya, SIGKILLtidak akan tiba sampai proses bangun. Ini bisa berpotensi tidak pernah, jika tidak ada I / O terjadi.
sapi
7

Jangan panik.

Tidak ada yang funky terjadi. Tidak ada bug kernel di sini. Ini adalah perilaku normal dari shell Bourne Again dan sistem operasi multitasking.

Yang perlu diingat adalah bahwa suatu proses membunuh dirinya sendiri , bahkan sebagai respons terhadapnya SIGKILL. Apa yang terjadi di sini adalah bahwa cangkang Bourne Again sedang menyelesaikan beberapa hal sebelum proses yang dikatakannya untuk membunuh dirinya sendiri untuk membunuh dirinya sendiri.

Pertimbangkan apa yang terjadi dari titik di mana yestelah dihentikan SIGTSTPdan Anda baru saja menjalankan killperintah dengan shell Bourne Again:

  1. Shell mengirim SIGKILLke yesproses.
  2. Secara paralel :
    1. The yesProses dijadwalkan untuk berjalan dan segera membunuh dirinya sendiri.
    2. Shell Bourne Again berlanjut, mengeluarkan perintah lain.

Alasan Anda melihat satu hal dan orang lain melihat yang lain adalah perlombaan sederhana antara dua proses yang siap dijalankan, pemenangnya sepenuhnya bergantung pada hal-hal yang bervariasi baik dari mesin ke mesin dan dari waktu ke waktu. Beban sistem membuat perbedaan, seperti halnya fakta bahwa CPU Anda virtual.

Dalam kasus yang menarik, detail dari langkah # 2 adalah ini:

  1. Shell Bourne Again berlanjut.
  2. Sebagai bagian dari internal perintah built-in killitu menandai entri di tabel tugasnya sebagai memerlukan pesan pemberitahuan dicetak pada titik berikutnya yang tersedia.
  3. Itu menyelesaikan killperintah, dan tepat sebelum mencetak prompt lagi memeriksa untuk melihat apakah itu harus mencetak pesan pemberitahuan tentang pekerjaan apa pun.
  4. The yesProses tidak memiliki kesempatan untuk membunuh dirinya sendiri lagi, jadi sejauh shell yang bersangkutan pekerjaan masih dalam keadaan berhenti. Jadi shell mencetak garis status pekerjaan "Dihentikan" untuk pekerjaan itu, dan mengatur ulang pemberitahuannya yang tertunda.
  5. The yesProses akan dijadwalkan dan membunuh dirinya sendiri.
  6. Kernel menginformasikan shell, yang sedang sibuk menjalankan editor baris perintahnya, bahwa proses telah membunuh dirinya sendiri. Shell mencatat perubahan status dan menandai pekerjaan sebagai pemberitahuan yang tertunda lagi.
  7. Cukup dengan menekan enteruntuk menggilir kembali melalui pencetakan prompt lagi memberi shell kesempatan untuk mencetak status pekerjaan baru.

Poin-poin penting adalah:

  • Proses bunuh diri. SIGKILLtidak ajaib. Proses memeriksa sinyal yang tertunda saat kembali ke mode aplikasi dari mode kernel, yang terjadi pada akhir kesalahan halaman, interupsi (tidak bersarang), dan panggilan sistem. Satu-satunya hal yang istimewa adalah bahwa kernel tidak mengizinkan tindakan sebagai tanggapan SIGKILLselain bunuh diri langsung dan tanpa syarat, tanpa kembali ke mode aplikasi. Yang penting, proses-proses harus membuat transisi mode-ke-aplikasi-mode dan dijadwalkan untuk berjalan untuk merespons sinyal.
  • CPU virtual hanyalah utas pada sistem operasi host. Tidak ada jaminan bahwa tuan rumah telah menjadwalkan CPU virtual untuk dijalankan. Sistem operasi host juga tidak ajaib.
  • Pesan pemberitahuan tidak dicetak ketika perubahan status pekerjaan terjadi (kecuali jika Anda menggunakan set -o notify). Mereka dicetak ketika shell berikutnya mencapai titik dalam siklus eksekusi yang memeriksa untuk melihat apakah ada notifikasi yang tertunda.
  • Bendera yang menunggu pemberitahuan sedang diset dua kali, sekali oleh killdan sekali oleh SIGCHLDpenangan sinyal. Ini berarti bahwa seseorang dapat melihat dua pesan jika shell berjalan sebelum yesproses dijadwal ulang untuk bunuh diri; satu pesan "Dihentikan" dan satu pesan "Dibunuh".
  • Jelas, /bin/killprogram tidak memiliki akses ke tabel pekerjaan internal shell; jadi Anda tidak akan melihat perilaku seperti itu dengan /bin/kill. Bendera yang menunggu pemberitahuan hanya diatur satu kali, oleh SIGCHLDpawang.
  • Untuk alasan yang sama, Anda tidak akan melihat perilaku ini jika Anda killyang yesproses dari shell lain.
JdeBP
sumber
3
Itu teori yang menarik, tetapi OP bisa mengetik jobsdan shell masih melihat prosesnya hidup. Itu akan menjadi satu kondisi lomba penjadwalan yang luar biasa panjang. :)
lcd047
3
Pertama-tama, terima kasih atas jawaban elaboratif Anda! Saya tentu masuk akal dan membersihkan beberapa hal .. Tapi seperti yang dinyatakan di atas, saya dapat menjalankan jobsperintah multiply setelah killsemua yang masih menunjukkan proses hanya berhenti. Namun Anda mengilhami saya untuk terus bereksperimen dan saya menemukan ini: pesan [1]+ Terminated yesdicetak segera setelah saya menjalankan perintah eksternal lain (bukan shell yang dibangun seperti echoatau jobs). Jadi saya bisa menjalankan jobssebanyak yang saya suka dan terus mencetak [1]+ Stopped yes. Tetapi segera setelah saya menjalankan lsmisalnya, Bash mencetak[1]+ Terminated yes
s1m0n
lcd047 tidak membaca komentar Anda untuk pertanyaan; yang penting dan seharusnya diedit ke awal pertanyaan, dengan benar. Sangat mudah untuk membebani sistem operasi host sehingga tamu tampaknya menjadwalkan dengan sangat aneh, dari dalam. Sama seperti ini, dan lebih dari itu. (Saya pernah berhasil menyebabkan penjadwalan yang sangat aneh dengan Bing Desktop yang sedang berjalan dan menghabiskan sebagian besar waktu CPU host.)
JdeBP
1
@Gilles Masalahnya tampaknya jobstidak menyadari bahwa prosesnya telah benar-benar mati ... Tidak yakin apa yang harus dilakukan tentang status yang diperbarui dengan menjalankan perintah lain.
lcd047
1
Bahkan Gilles tidak melihat komentar itu. Inilah sebabnya mengapa Anda harus meletakkan hal-hal penting semacam ini dalam pertanyaan , bukan menguburnya dalam komentar. Gilles, jawabannya jelas berbicara tentang keterlambatan pengiriman sinyal, bukan keterlambatan pengirimannya . Anda telah mencampuradukkannya. Juga, baca komentar si penanya (dan memang poin utama yang diberikan di sini) dan lihat asumsi fundamental salah yang sangat penting yang Anda buat. Prosesor virtual tidak harus berjalan berbaris, dan tidak secara ajaib mampu selalu berjalan dengan kecepatan penuh.
JdeBP
2

Sesuatu yang funky mungkin terjadi pada sistem Anda, pada resep saya resep Anda bekerja dengan baik dengan dan tanpa -9:

> yes
...
^Z
[1]+  Stopped                 yes
> jobs
[1]+  Stopped                 yes
> kill %1
[1]+  Killed                  yes
> jobs
> 

Dapatkan pid jobs -pdan coba untuk membunuhnya root.

Dan Cornilescu
sumber
Bolehkah saya bertanya versi distribusi / kernel / bash apa yang Anda gunakan? Mungkin killperintah internal bash Anda bekerja lebih keras dan memeriksa apakah pekerjaan itu dibekukan (Anda mungkin ingin mencoba mencari tahu PID pekerjaan itu dan membunuhnya menggunakan itu env kill <pid>. Dengan begitu Anda akan menggunakan killperintah yang sebenarnya dan bukan bash builtin.
mreithub
bash-4.2-75.3.1.x86_64 pada opensuse 13.2. which kill /usr/bin/kill
Cmd
1
whichbukan bash-builtin, jadi which <anything>akan selalu memberi Anda jalan ke perintah yang sebenarnya. Tapi coba membandingkan kill --helpvs /usr/bin/kill --help.
mreithub
Ah benar Memang, itu builtin kill.
Dan Cornilescu
2

Yang Anda amati adalah bug dalam versi bash ini.

kill -9 %1tidak membunuh pekerjaan itu segera. Anda dapat mengamati itu dengan ps. Anda dapat melacak proses bash untuk melihat kapan killpanggilan sistem dipanggil, dan melacak subproses untuk melihat kapan ia menerima dan memproses sinyal. Lebih menarik lagi, Anda dapat pergi dan melihat apa yang terjadi pada proses.

bash-4.3$ sleep 9999
^Z
[1]+  Stopped                 sleep 9999
bash-4.3$ kill -9 %1

[1]+  Stopped                 sleep 9999
bash-4.3$ jobs
[1]+  Stopped                 sleep 9999
bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ 

Di terminal lain:

% ps 3083
  PID TTY      STAT   TIME COMMAND
 3083 pts/4    Z      0:00 [sleep] <defunct>

Subproses adalah zombie . Sudah mati: yang tersisa hanyalah entri dalam tabel proses (tetapi tidak ada memori, kode, buka file, dll.). Entri dibiarkan sampai induknya memperhatikan dan mengambil status keluar dengan memanggil waitpanggilan sistem atau salah satu saudara kandungnya .

Shell interaktif seharusnya memeriksa anak-anak yang mati dan menuai mereka sebelum mencetak prompt (kecuali jika dikonfigurasi sebaliknya). Versi bash ini gagal melakukannya dalam beberapa keadaan:

bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ true
bash-4.3$ /bin/true
[1]+  Killed                  sleep 9999

Anda mungkin mengharapkan bash untuk melaporkan "Dibunuh" segera setelah mencetak prompt setelah killperintah, tetapi itu tidak dijamin, karena ada kondisi balapan. Sinyal dikirimkan secara tidak sinkron: killpanggilan sistem kembali segera setelah kernel menentukan proses pengiriman sinyal, tanpa menunggu pengirimannya. Mungkin saja, dan dalam praktiknya, bash mempunyai waktu untuk memeriksa status subprosesnya, menemukan bahwa itu masih belum mati ( wait4tidak melaporkan kematian anak), dan mencetak bahwa prosesnya masih dihentikan. Apa yang salah adalah bahwa sebelum prompt berikutnya, sinyal telah dikirim ( psmelaporkan bahwa prosesnya mati), namun bash masih belum memanggilwait4(kita bisa melihat itu bukan hanya karena masih melaporkan pekerjaan sebagai "Berhenti", tetapi karena zombie masih ada di tabel proses). Bahkan, bash hanya menuai zombie saat berikutnya perlu memanggil wait4, ketika menjalankan beberapa perintah eksternal lainnya.

Bugnya berselang dan saya tidak bisa mereproduksinya saat bash dilacak (mungkin karena ini adalah kondisi balapan di mana bash perlu bereaksi cepat). Jika sinyal dikirim sebelum bash memeriksa, semuanya terjadi seperti yang diharapkan.

Gilles 'SANGAT berhenti menjadi jahat'
sumber