Misalkan saya memiliki proses yang memunculkan satu proses anak. Sekarang ketika proses orangtua keluar karena alasan apa pun (biasanya atau tidak normal, dengan membunuh, ^ C, menyatakan kegagalan atau apa pun), saya ingin proses anak mati. Bagaimana cara melakukannya dengan benar?
Beberapa pertanyaan serupa tentang stackoverflow:
- (bertanya sebelumnya) Bagaimana saya dapat menyebabkan proses anak keluar ketika orang tua melakukannya?
- (ditanyakan kemudian) Apakah proses anak yang dibuat dengan garpu () secara otomatis terbunuh ketika orang tua terbunuh?
Beberapa pertanyaan serupa tentang stackoverflow untuk Windows :
prctl()
dalam kondisi bebas lomba. Btw, jawaban yang ditautkan oleh Maxim salah.man prctl
mengatakan: Atur orang tua memproses sinyal kematian dari proses panggilan ke arg2 (baik nilai sinyal dalam kisaran 1..maxsig, atau 0 untuk menghapus). Ini adalah sinyal bahwa proses panggilan akan didapat ketika orang tuanya meninggal. Nilai ini dihapus untuk anak dari fork (2) dan (sejak Linux 2.4.36 / 2.6.23) ketika mengeksekusi biner set-user-ID atau set-group-ID.Saya mencoba untuk memecahkan masalah yang sama, dan karena program saya harus berjalan pada OS X, solusi hanya Linux tidak bekerja untuk saya.
Saya sampai pada kesimpulan yang sama dengan orang lain di halaman ini - tidak ada cara yang kompatibel dengan POSIX untuk memberi tahu seorang anak ketika orang tua meninggal. Jadi saya membereskan hal terbaik berikutnya - mengadakan pemilihan anak.
Ketika proses orang tua meninggal (karena alasan apa pun) proses orang tua anak menjadi proses 1. Jika anak hanya melakukan jajak pendapat secara berkala, ia dapat memeriksa apakah orang tuanya adalah 1. Jika demikian, anak tersebut harus keluar.
Ini tidak bagus, tetapi berfungsi, dan lebih mudah daripada solusi polling TCP socket / lockfile yang disarankan di tempat lain di halaman ini.
sumber
gettpid()
tidak menjadi 1 tetapi mendapatkanpid
penjadwal zona (proseszsched
).Saya telah mencapai ini di masa lalu dengan menjalankan kode "asli" di "child" dan kode "spawned" di "parent" (yaitu: Anda membalikkan arti tes yang biasa setelah
fork()
). Kemudian jebak SIGCHLD dalam kode "spawned" ...Mungkin tidak mungkin dalam kasus Anda, tetapi lucu ketika berfungsi.
sumber
Jika Anda tidak dapat mengubah proses anak, Anda dapat mencoba sesuatu seperti berikut:
Ini menjalankan anak dari dalam proses shell dengan kontrol pekerjaan diaktifkan. Proses anak lahir di latar belakang. Shell menunggu baris baru (atau EOF) lalu membunuh anak itu.
Ketika orang tua meninggal - tidak peduli apa alasannya - itu akan menutup ujung pipa. Cangkang anak akan mendapatkan EOF dari bacaan dan melanjutkan untuk membunuh proses anak yang berlatar belakang.
sumber
dup2
dan mengambil alih stdin dengan menggunakanread -u
flag untuk membaca dari deskriptor file tertentu. Saya juga menambahkansetpgid(0, 0)
anak untuk mencegahnya keluar saat menekan ^ C di terminal.dup2()
panggilan itu salah. Jika Anda ingin menggunakanpipes[0]
sebagai stdin Anda harus menulisdup2(pipes[0], 0)
alih-alihdup2(0, pipes[0])
. Didup2(oldfd, newfd)
sinilah panggilan menutup newfd yang sebelumnya terbuka.Di Linux, Anda dapat memasang sinyal kematian orang tua pada anak, misalnya:
Perhatikan bahwa menyimpan id proses induk sebelum garpu dan mengujinya pada anak setelah
prctl()
menghilangkan kondisi ras antaraprctl()
dan keluar dari proses yang disebut anak.Perhatikan juga bahwa sinyal kematian orang tua dari anak tersebut dihapus pada anak-anak yang baru dibuat sendiri. Itu tidak terpengaruh oleh
execve()
.Tes itu dapat disederhanakan jika kita yakin bahwa proses sistem yang bertugas mengadopsi semua anak yatim memiliki PID 1:
Mengandalkan proses sistem yang sedang
init
dan memiliki PID 1 tidak mudah dibawa. POSIX.1-2008 menetapkan :Secara tradisional, proses sistem yang mengadopsi semua anak yatim adalah PID 1, yaitu init - yang merupakan nenek moyang dari semua proses.
Pada sistem modern seperti Linux atau FreeBSD proses lain mungkin memiliki peran itu. Misalnya, di Linux, suatu proses dapat memanggil
prctl(PR_SET_CHILD_SUBREAPER, 1)
untuk memantapkan dirinya sebagai proses sistem yang mewarisi semua anak yatim dari keturunannya (lih. Contoh pada Fedora 25).sumber
init(8)
diproses .... satu-satunya hal yang dapat Anda asumsikan adalah bahwa ketika proses induk mati, adalah bahwa id induknya akan berubah. Ini sebenarnya terjadi sekali dalam kehidupan suatu proses .... dan adalah ketika proses 'orang tua meninggal. Hanya ada satu pengecualian utama untuk ini, dan untukinit(8)
anak - anak, tetapi Anda terlindungi dari ini, karenainit(8)
tidak pernahexit(2)
(panik dalam hal ini)Demi kelengkapan. Di macOS Anda dapat menggunakan kqueue:
sumber
NSTask
atau memunculkan posix . LihatstartTask
fungsi dalam kode saya di sini: github.com/neoneye/newton-commander-browse/blob/master/Classes/…Apakah proses anak memiliki pipa ke / dari proses induk? Jika demikian, Anda akan menerima SIGPIPE jika menulis, atau dapatkan EOF saat membaca - kondisi ini dapat dideteksi.
sumber
Terinspirasi oleh jawaban lain di sini, saya datang dengan solusi all-POSIX berikut. Gagasan umum adalah menciptakan proses peralihan antara orangtua dan anak, yang memiliki satu tujuan: Perhatikan ketika orangtua meninggal, dan secara eksplisit membunuh anak itu.
Jenis solusi ini berguna ketika kode pada anak tidak dapat dimodifikasi.
Ada dua peringatan kecil dengan metode ini:
Selain itu, kode aktual yang saya gunakan adalah dalam Python. Ini dia untuk kelengkapan:
sumber
Saya tidak percaya itu mungkin untuk menjamin bahwa hanya menggunakan panggilan POSIX standar. Seperti kehidupan nyata, begitu seorang anak dilahirkan, ia memiliki kehidupannya sendiri.
Hal ini dimungkinkan untuk proses induk untuk menangkap yang paling mungkin peristiwa pemutusan, dan berusaha untuk membunuh proses anak pada saat itu, tapi selalu ada beberapa yang tidak bisa ditangkap.
Misalnya, tidak ada proses yang dapat menangkap a
SIGKILL
. Ketika kernel menangani sinyal ini, ia akan mematikan proses yang ditentukan tanpa pemberitahuan apa pun terhadap proses itu.Untuk memperluas analogi - satu-satunya cara standar lain untuk melakukannya adalah bagi anak untuk bunuh diri ketika menemukan bahwa ia tidak lagi memiliki orangtua.
Ada cara khusus untuk melakukannya dengan Linux
prctl(2)
- lihat jawaban lain.sumber
Seperti yang ditunjukkan oleh orang lain, mengandalkan pid orang tua menjadi 1 ketika orang tua keluar adalah non-portabel. Alih-alih menunggu ID proses induk tertentu, tunggulah hingga ID berubah:
Tambahkan tidur mikro seperti yang diinginkan jika Anda tidak ingin polling dengan kecepatan penuh.
Pilihan ini tampaknya lebih sederhana bagi saya daripada menggunakan pipa atau mengandalkan sinyal.
sumber
getpid()
dilakukan pada orang tua sebelum meneleponfork()
. Jika orangtua meninggal sebelum itu, anak itu tidak ada. Apa yang mungkin terjadi adalah anak keluar hidup dengan orang tua untuk sementara waktu.Pasang penjebak perangkap untuk menangkap SIGINT, yang membunuh proses anak Anda jika masih hidup, meskipun poster lain benar bahwa itu tidak akan menangkap SIGKILL.
Buka file .lockfile dengan akses eksklusif dan minta polling anak mencoba membukanya - jika terbuka, proses anak harus keluar
sumber
Solusi ini bekerja untuk saya:
Ini untuk proses tipe pekerja yang keberadaannya hanya masuk akal ketika orang tua masih hidup.
sumber
Saya pikir cara cepat dan kotor adalah membuat pipa antara anak dan orang tua. Ketika orang tua keluar, anak-anak akan menerima SIGPIPE.
sumber
Beberapa poster telah menyebutkan pipa dan
kqueue
. Bahkan Anda juga dapat membuat sepasang soket domain Unix yang terhubung melaluisocketpair()
panggilan. Jenis soket seharusnyaSOCK_STREAM
.Biarkan kami mengira Anda memiliki dua deskriptor file socket fd1, fd2. Sekarang
fork()
untuk membuat proses anak, yang akan mewarisi fds. Pada orang tua Anda menutup fd2 dan pada anak Anda menutup fd1. Sekarang setiap proses dapatpoll()
membuka fd yang tersisa pada akhirPOLLIN
acara. Selama masing-masing pihak tidak secara eksplisitclose()
menemukan selama masa normal, Anda dapat cukup yakin bahwa sebuahPOLLHUP
bendera harus mengindikasikan pemutusan pihak lain (tidak masalah bersih atau tidak). Setelah diberitahu tentang peristiwa ini, anak dapat memutuskan apa yang harus dilakukan (misalnya mati).Anda dapat mencoba mengkompilasi kode proof-of-concept di atas, dan menjalankannya di terminal seperti
./a.out &
. Anda memiliki sekitar 100 detik untuk bereksperimen dengan membunuh PID induk dengan berbagai sinyal, atau itu hanya akan keluar. Dalam kedua kasus, Anda akan melihat pesan "child: parent hung up".Dibandingkan dengan metode yang menggunakan
SIGPIPE
handler, metode ini tidak perlu mencobawrite()
panggilan.Metode ini juga simetris , yaitu proses dapat menggunakan saluran yang sama untuk memantau keberadaan satu sama lain.
Solusi ini hanya memanggil fungsi POSIX. Saya mencoba ini di Linux dan FreeBSD. Saya pikir itu harus bekerja pada Unix lain tetapi saya belum benar-benar diuji
Lihat juga:
unix(7)
Linux halaman manual,unix(4)
untuk FreeBSD,poll(2)
,socketpair(2)
,socket(7)
di Linux.sumber
Di bawah POSIX , yang
exit()
,_exit()
dan_Exit()
fungsi didefinisikan untuk:Jadi, jika Anda mengatur agar proses induk menjadi proses kontrol untuk grup prosesnya, anak harus mendapatkan sinyal SIGHUP saat orangtua keluar. Saya tidak benar-benar yakin itu terjadi ketika orang tua crash, tapi saya pikir itu terjadi. Tentu saja, untuk kasus-kasus non-crash, itu akan berfungsi dengan baik.
Perhatikan bahwa Anda mungkin harus membaca banyak cetak kecil - termasuk bagian Definisi Dasar (Definisi), serta informasi Layanan Sistem untuk
exit()
dansetsid()
dansetpgrp()
- untuk mendapatkan gambaran lengkap. (Begitu juga aku!)sumber
Jika Anda mengirim sinyal ke pid 0, gunakan misalnya
sinyal itu dikirim ke seluruh kelompok proses, sehingga secara efektif membunuh anak.
Anda dapat mengujinya dengan mudah seperti:
Jika Anda menekan ^ D, Anda akan melihat teks
"Terminated"
sebagai indikasi bahwa penerjemah Python memang telah terbunuh, alih-alih hanya keluar karena stdin ditutup.sumber
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
dengan senang hati mencetak 4 bukannya DihentikanDalam hal ini relevan untuk orang lain, ketika saya menelurkan contoh JVM dalam proses anak bercabang dari C ++, satu-satunya cara saya bisa mendapatkan contoh JVM untuk berakhir dengan benar setelah proses induk selesai adalah untuk melakukan hal berikut. Semoga seseorang dapat memberikan umpan balik dalam komentar jika ini bukan cara terbaik untuk melakukan ini.
1) Panggil
prctl(PR_SET_PDEATHSIG, SIGHUP)
proses anak bercabang seperti yang disarankan sebelum meluncurkan aplikasi Java viaexecv
, dan2) Tambahkan hook shutdown ke aplikasi Java yang melakukan polling hingga PID induknya sama dengan 1, lalu lakukan dengan keras
Runtime.getRuntime().halt(0)
. Polling dilakukan dengan meluncurkan shell terpisah yang menjalankanps
perintah (Lihat: Bagaimana cara menemukan PID saya di Jawa atau JRuby di Linux? ).EDIT 130118:
Tampaknya itu bukan solusi yang kuat. Saya masih berjuang sedikit untuk memahami nuansa dari apa yang terjadi, tetapi saya kadang-kadang masih mendapatkan proses JVM yatim ketika menjalankan aplikasi ini dalam sesi layar / SSH.
Alih-alih polling untuk PPID di aplikasi Java, saya hanya meminta hook shutdown melakukan pembersihan diikuti dengan penghentian keras seperti di atas. Kemudian saya memastikan untuk memohon
waitpid
dalam aplikasi induk C ++ pada proses anak yang dilahirkan ketika tiba saatnya untuk menghentikan semuanya. Ini tampaknya menjadi solusi yang lebih kuat, karena proses anak memastikan bahwa itu berakhir, sementara orangtua menggunakan referensi yang ada untuk memastikan bahwa anak-anaknya berakhir. Bandingkan ini dengan solusi sebelumnya yang membuat proses orangtua berakhir kapan saja, dan mintalah anak-anak mencoba mencari tahu apakah mereka telah yatim piatu sebelum diakhiri.sumber
PID equals 1
menunggu tidak valid. Induk baru bisa berupa PID lain. Anda harus memeriksa apakah itu berubah dari induk asli (getpid () sebelum garpu ()) ke induk baru (getppid () pada anak tidak sama dengan getpid () ketika dipanggil sebelum garpu ()).Cara lain untuk melakukan ini yang spesifik Linux adalah membuat orang tua dibuat di namespace PID baru. Maka akan menjadi PID 1 di namespace itu, dan ketika keluar semua anak-anak itu akan segera dibunuh
SIGKILL
.Sayangnya, untuk membuat namespace PID baru Anda harus memiliki
CAP_SYS_ADMIN
. Namun, metode ini sangat efektif dan tidak memerlukan perubahan nyata pada orang tua atau anak-anak di luar peluncuran awal orang tua.Lihat clone (2) , pid_namespaces (7) , dan unshare (2) .
sumber
Jika orang tua meninggal, PPID anak yatim berubah menjadi 1 - Anda hanya perlu memeriksa PPID Anda sendiri. Di satu sisi, ini adalah polling, yang disebutkan di atas. di sini adalah potongan shell untuk itu:
sumber
Saya menemukan 2 solusi, keduanya tidak sempurna.
1.Bunuh semua anak dengan cara dibunuh (-pid) saat menerima sinyal SIGTERM.
Jelas, solusi ini tidak dapat menangani "kill -9", tetapi ia bekerja untuk sebagian besar kasus dan sangat sederhana karena tidak perlu mengingat semua proses anak.
Dengan cara yang sama, Anda dapat menginstal 'keluar' handler seperti cara di atas jika Anda memanggil process.exit di suatu tempat. Catatan: Ctrl + C dan crash tiba-tiba telah secara otomatis diproses oleh OS untuk mematikan grup proses, jadi tidak ada lagi di sini.
2.Use chjj / pty.js untuk bertelur proses Anda dengan mengendalikan terminal terpasang.
Ketika Anda membunuh proses saat ini dengan cara apapun bahkan membunuh -9, semua proses anak akan secara otomatis terbunuh juga (oleh OS?). Saya kira itu karena proses saat ini memegang sisi lain terminal, jadi jika proses saat ini mati, proses anak akan mendapatkan SIGPIPE sehingga mati.
sumber
Saya berhasil melakukan solusi portabel, non-polling dengan 3 proses dengan menyalahgunakan kontrol terminal dan sesi. Ini adalah masturbasi mental, tetapi berhasil.
Caranya adalah:
Seperti itu:
Kekurangan:
sumber
Meskipun 7 tahun telah berlalu, saya baru saja mengalami masalah ini karena saya menjalankan aplikasi SpringBoot yang perlu memulai webpack-dev-server selama pengembangan dan perlu membunuhnya ketika proses backend berhenti.
Saya mencoba menggunakan
Runtime.getRuntime().addShutdownHook
tetapi berfungsi pada Windows 10 tetapi tidak pada Windows 7.Saya telah mengubahnya untuk menggunakan utas khusus yang menunggu proses untuk keluar atau
InterruptedException
yang tampaknya berfungsi dengan baik pada kedua versi Windows.sumber
Secara historis, dari UNIX v7, sistem proses telah mendeteksi orphanity proses dengan memeriksa id induk suatu proses. Seperti yang saya katakan, secara historis,
init(8)
proses sistem adalah proses khusus hanya dengan satu alasan: Tidak bisa mati. Itu tidak bisa mati karena algoritma kernel untuk berurusan dengan menetapkan proses id induk baru, tergantung pada fakta ini. ketika suatu proses mengeksekusiexit(2)
panggilannya (melalui pemanggilan sistem proses atau dengan tugas eksternal sebagai mengirimkan sinyal atau sejenisnya) kernel menugaskan kembali semua anak dari proses ini id dari proses init sebagai id proses orang tua mereka. Ini mengarah pada tes yang paling mudah, dan cara yang paling portabel untuk mengetahui apakah suatu proses telah mendapatkan anak yatim. Cukup periksa hasil darigetppid(2)
panggilan sistem dan jika itu adalah id proses dariinit(2)
proses maka proses mendapat yatim sebelum panggilan sistem.Dua masalah muncul dari pendekatan ini yang dapat menyebabkan masalah:
init
proses menjadi proses pengguna apa pun, jadi Bagaimana kami dapat memastikan bahwa proses init akan selalu menjadi induk dari semua proses anak yatim? Nah, dalamexit
kode panggilan sistem ada pemeriksaan eksplisit untuk melihat apakah proses mengeksekusi panggilan adalah proses init (proses dengan pid sama dengan 1) dan jika itu masalahnya, kernel panik (Seharusnya tidak bisa lagi mempertahankan hirarki proses) sehingga tidak diizinkan untuk proses init untuk melakukanexit(2)
panggilan.1
, tetapi itu tidak dijamin oleh pendekatan POSIX, yang menyatakan (sebagaimana diekspos dalam respons lain) bahwa hanya id proses sistem yang dicadangkan untuk tujuan itu. Hampir tidak ada implementasi posix yang melakukan ini, dan Anda dapat mengasumsikan dalam sistem turunan unix asli yang memiliki1
respongetppid(2)
panggilan sistem sudah cukup untuk menganggap prosesnya yatim. Cara lain untuk mengecek adalah dengan membuatgetppid(2)
setelah garpu dan membandingkan nilai itu dengan hasil dari panggilan baru. Ini sama sekali tidak bekerja dalam semua kasus, karena kedua panggilan tidak bersama-sama atomik, dan proses induk bisa mati setelahfork(2)
dan sebelumgetppid(2)
panggilan sistem pertama . Prosesparent id only changes once, when its parent does an
keluar (2)call, so this should be enough to check if the
getppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of
init (8) `, tetapi Anda dapat mengasumsikan dengan aman proses-proses ini tidak memiliki orangtua juga (kecuali ketika Anda mengganti dalam sistem proses init)sumber
Saya telah melewati pid orangtua menggunakan lingkungan ke anak, kemudian secara berkala memeriksa apakah / proc / $ ppid ada dari anak.
sumber