Bagaimana Linux “membunuh” suatu proses?

91

Sering membingungkan saya bahwa, walaupun saya telah bekerja secara profesional dengan komputer selama beberapa dekade dan Linux selama satu dekade, saya sebenarnya memperlakukan sebagian besar fungsi OS sebagai kotak hitam, tidak seperti sulap.

Hari ini saya memikirkan killperintah itu, dan sementara saya menggunakannya berkali-kali per hari (keduanya dalam "normal" dan -9rasanya) saya harus mengakui bahwa saya sama sekali tidak tahu cara kerjanya di belakang layar.

Dari sudut pandang saya, jika proses yang berjalan "digantung", saya memanggil killPID-nya, dan kemudian tiba-tiba tidak berjalan lagi. Sihir!

Apa yang sebenarnya terjadi di sana? Halaman manual berbicara tentang "sinyal" tetapi tentu saja itu hanya abstraksi. Mengirim kill -9ke suatu proses tidak memerlukan kerja sama proses (seperti menangani sinyal), hanya mematikannya.

  • Bagaimana Linux menghentikan proses dari melanjutkan untuk mengambil waktu CPU?
  • Apakah dihapus dari penjadwalan?
  • Apakah itu memutuskan proses dari pegangan file yang terbuka?
  • Bagaimana proses memori virtual dilepaskan?
  • Apakah ada sesuatu seperti tabel global dalam memori, di mana Linux menyimpan referensi ke semua sumber daya yang diambil oleh suatu proses, dan ketika saya "membunuh" suatu proses, Linux hanya melewati tabel itu dan membebaskan sumber daya satu per satu?

Saya benar-benar ingin tahu semua itu!

seratoninant
sumber

Jawaban:

72

Mengirim kill -9 ke suatu proses tidak memerlukan kerja sama proses (seperti menangani sinyal), itu hanya mematikannya.

Anda mengira itu karena beberapa sinyal dapat ditangkap dan diabaikan mereka semua melibatkan kerja sama. Tetapi sesuai man 2 signal, " sinyal SIGKILL dan SIGSTOP tidak dapat ditangkap atau diabaikan". SIGTERM dapat ditangkap, itulah sebabnya dataran killtidak selalu efektif - umumnya ini berarti sesuatu dalam handler proses menjadi serba salah. 1

Jika suatu proses tidak (atau tidak bisa) mendefinisikan handler untuk sinyal yang diberikan, kernel melakukan tindakan default. Dalam kasus SIGTERM dan SIGKILL, ini adalah untuk menghentikan proses (kecuali PID nya adalah 1; kernel tidak akan berakhir init) 2 artinya file menangani, ditutup, memorinya dikembalikan ke kumpulan sistem, orang tuanya menerima SIGCHILD, anak yatim piatu anak-anak diwarisi oleh init, dll., sama seperti ia memanggil exit(lihat man 2 exit). Proses tidak lagi ada - kecuali jika berakhir sebagai zombie, dalam hal ini masih terdaftar di tabel proses kernel dengan beberapa informasi; itu terjadi ketika induknya tidakwaitdan menangani informasi ini dengan benar. Namun, proses zombie tidak lagi memiliki memori yang dialokasikan untuk mereka dan karenanya tidak dapat terus dijalankan.

Apakah ada sesuatu seperti tabel global dalam memori di mana Linux menyimpan referensi ke semua sumber daya yang diambil oleh suatu proses dan ketika saya "membunuh" suatu proses Linux hanya melalui tabel itu dan membebaskan sumber daya satu per satu?

Saya pikir itu cukup akurat. Memori fisik dilacak oleh halaman (satu halaman biasanya sama dengan potongan 4 KB) dan halaman-halaman itu diambil dari dan dikembalikan ke kumpulan global. Ini sedikit lebih rumit karena beberapa halaman yang dibebaskan di-cache jika data yang dikandungnya diperlukan lagi (yaitu, data yang dibaca dari file yang masih ada).

Halaman manual berbicara tentang "sinyal" tetapi tentu saja itu hanya abstraksi.

Tentu, semua sinyal adalah abstraksi. Mereka konseptual, seperti "proses". Saya bermain semantik sedikit, tetapi jika Anda maksud SIGKILL secara kualitatif berbeda dari SIGTERM, maka ya dan tidak. Ya dalam arti bahwa itu tidak dapat ditangkap, tetapi tidak dalam arti bahwa keduanya adalah sinyal. Secara analogi, sebuah apel bukanlah jeruk, tetapi apel dan jeruk, menurut definisi yang sudah ada sebelumnya, keduanya adalah buah. SIGKILL tampaknya lebih abstrak karena Anda tidak dapat menangkapnya, tetapi ini masih merupakan sinyal. Berikut adalah contoh penanganan SIGTERM, saya yakin Anda pernah melihat ini sebelumnya:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}

Proses ini hanya akan tidur selamanya. Anda dapat menjalankannya di terminal dan mengirimkannya dengan SIGTERM kill. Ini memuntahkan hal-hal seperti:

Received 15 from pid 25331, uid 1066.

1066 adalah UID saya. PID akan berupa shell yang killdieksekusi, atau PID kill jika Anda bercabang ( kill 25309 & echo $?).

Sekali lagi, tidak ada gunanya menetapkan handler untuk SIGKILL karena itu tidak akan berfungsi. 3 Jika saya kill -9 25309proses akan berakhir. Tapi itu masih merupakan sinyal; kernel memiliki informasi tentang siapa yang mengirim sinyal , jenis sinyal apa itu, dll.


1. Jika Anda belum melihat daftar sinyal yang mungkin , lihat kill -l.

2. Pengecualian lain, seperti yang disebutkan oleh Tim Post di bawah ini, berlaku untuk proses tidur yang tidak terputus . Ini tidak dapat dibangunkan sampai masalah mendasar diselesaikan, dan demikian pula SEMUA sinyal (termasuk SIGKILL) ditangguhkan untuk durasi tersebut. Namun, suatu proses tidak dapat menciptakan situasi itu dengan sengaja.

3. Ini tidak berarti menggunakan kill -9adalah hal yang lebih baik untuk dilakukan dalam praktek. Handler contoh saya adalah yang buruk dalam arti bahwa itu tidak mengarah exit(). Tujuan sebenarnya dari pengendali SIGTERM adalah untuk memberikan proses kesempatan untuk melakukan hal-hal seperti membersihkan file sementara, kemudian keluar secara sukarela. Jika Anda menggunakan kill -9, itu tidak mendapatkan kesempatan ini, jadi hanya lakukan itu jika bagian "keluar secara sukarela" tampaknya telah gagal.

goldilocks
sumber
Ok tapi apa yang membunuh proses dengan -9karena itu masalah sebenarnya bagaimana yang menginginkan bahwa yang satu ini harus mati! ;)
Kiwy
@ Kyiwy: Kernel. IPC termasuk sinyal yang melewatinya; kernel mengimplementasikan tindakan standar.
goldilocks
12
Mungkin perlu disebutkan bahwa disk sleep (D) mendahului semua sinyal, sementara prosesnya dalam keadaan itu. Oleh karena itu mencoba kill -9proses I / O terikat tertentu tidak akan berhasil, setidaknya tidak segera.
Pos Tim
7
Saya akan menambahkan bahwa karena kill -9tidak dapat ditangkap, proses menerimanya tidak dapat melakukan pembersihan (misalnya menghapus file sementara, membebaskan memori bersama, dll.) Sebelum keluar. Karenanya, gunakan kill -9(alias kill -kill) hanya sebagai pilihan terakhir. Mulailah dengan a kill -hupdan / atau kill -termpertama dan kemudian gunakan kill -killsebagai pukulan terakhir.
JRFerguson
"Proses tidak lagi ada - kecuali jika berakhir sebagai zombie, dalam hal ini masih terdaftar dalam tabel proses kernel dengan beberapa informasi" sebenarnya, semua proses masuk ke keadaan zombie ketika mereka mati, dan zombie akan menghilang ketika orang tua tidak menunggu anak, biasanya itu terjadi terlalu cepat bagi Anda untuk melihat hal itu terjadi
pandai
3

Setiap proses berjalan untuk waktu yang dijadwalkan dan kemudian terganggu oleh pengatur waktu perangkat keras, untuk memberikan inti CPU-nya untuk tugas-tugas lain. Inilah sebabnya mengapa dimungkinkan untuk memiliki lebih banyak proses daripada core CPU, atau bahkan menjalankan semua sistem operasi dengan banyak proses pada satu CPU inti tunggal.

Setelah proses terputus, kontrol kembali ke kode kernel. Kode itu kemudian dapat membuat keputusan untuk tidak melanjutkan pelaksanaan proses yang terputus, tanpa kerja sama dari sisi proses. kill -9 dapat mengakhiri eksekusi di setiap baris program Anda.

h22
sumber
0

Berikut adalah deskripsi ideal tentang cara membunuh suatu proses bekerja. Dalam praktiknya, varian Unix mana pun akan memiliki banyak komplikasi dan optimisasi tambahan.

Kernel memiliki struktur data untuk setiap proses yang menyimpan informasi tentang memori apa yang dipetakannya, thread apa yang dimilikinya dan kapan dijadwalkan, file apa yang dibuka, dll. Jika kernel memutuskan untuk mematikan suatu proses, kernel membuat catatan di struktur data proses (dan mungkin dalam struktur data masing-masing utas) bahwa proses tersebut akan dibunuh.

Jika salah satu utas proses saat ini dijadwalkan pada CPU lain, kernel mungkin memicu interupsi pada CPU lain untuk membuat utas itu berhenti mengeksekusi lebih cepat.

Ketika scheduler melihat bahwa utas sedang dalam proses yang harus dimatikan, penjadwal tidak akan menjadwalkannya lagi.

Ketika tidak ada utas proses yang dijadwalkan lagi, kernel mulai membebaskan sumber daya dari proses (memori, deskriptor file, ...). Setiap kali kernel membebaskan sumber daya, ia memeriksa apakah pemiliknya masih memiliki sumber daya hidup. Setelah proses tidak memiliki lagi sumber daya hidup (pemetaan memori, deskriptor file terbuka, ...), struktur data untuk proses itu sendiri dapat dibebaskan dan entri yang sesuai dapat dihapus dari tabel proses.

Beberapa sumber daya dapat segera dibebaskan (mis., Membatalkan alokasi memori yang tidak digunakan oleh operasi I / O). Sumber daya lain harus menunggu, misalnya data yang menggambarkan operasi I / O tidak dapat dibebaskan saat operasi I / O sedang berlangsung (saat DMA sedang berlangsung, memori yang diakses sedang digunakan, dan membatalkan DMA memerlukan menghubungi periferal). Pengemudi untuk sumber daya semacam itu diberitahukan, dan dapat mencoba untuk mempercepat pembatalan; begitu operasi tidak lagi berlangsung, pengemudi akan menyelesaikan pembebasan sumber daya itu.

(Entri dalam tabel proses sebenarnya adalah sumber daya yang dimiliki oleh proses induk, yang akan dibebaskan ketika proses mati dan orang tua mengakui acara tersebut .)

Gilles
sumber