Apa yang terjadi ketika mengirim SIGKILL ke Proses Zombie di Linux?

10

Di Linux, ketika proses anak berakhir dan orang tuanya belum menunggu, itu menjadi proses zombie. Kode keluar anak disimpan di deskriptor pid.

Jika a SIGKILLdikirim ke anak, seharusnya tidak ada efek apa pun.

Apakah ini berarti bahwa kode keluar tidak akan dimodifikasi oleh SIGKILLatau akankah kode keluar dimodifikasi untuk menunjukkan bahwa anak keluar karena menerima SIGKILL?

pengguna137481
sumber

Jawaban:

14

Untuk menjawab pertanyaan itu, Anda harus memahami bagaimana sinyal dikirim ke suatu proses dan bagaimana suatu proses ada di kernel.

Setiap proses direpresentasikan sebagai bagian task_structdalam kernel (definisi ada di sched.hfile header dan dimulai di sini ). Struct yang menyimpan informasi tentang proses; misalnya pid. Informasi penting ada pada baris 1566 di mana sinyal yang terkait disimpan. Ini diatur hanya jika sinyal dikirim ke proses.

Proses mati atau proses zombie masih memiliki task_struct. Struct tetap, sampai proses induk (alami atau dengan adopsi) telah memanggil wait()setelah menerima SIGCHLDuntuk menuai proses anaknya. Ketika sinyal dikirim, sinyal signal_structdisetel. Tidak masalah apakah sinyalnya mudah ditangkap atau tidak, dalam hal ini.

Sinyal dievaluasi setiap kali ketika proses berjalan. Atau tepatnya, sebelum proses akan berjalan. Prosesnya kemudian di TASK_RUNNINGnegara. Kernel menjalankan schedule()rutin yang menentukan proses berjalan berikutnya sesuai dengan algoritma penjadwalannya. Dengan asumsi proses ini adalah proses yang berjalan berikutnya, nilai signal_structdievaluasi, apakah ada sinyal menunggu untuk ditangani atau tidak. Jika penangan sinyal didefinisikan secara manual (melalui signal()atau sigaction()), fungsi terdaftar dieksekusi, jika bukan tindakan default sinyal dieksekusi. Tindakan default tergantung pada sinyal yang dikirim.

Misalnya, SIGSTOPpenangan default sinyal akan mengubah status proses saat ini menjadi TASK_STOPPEDdan kemudian menjalankan schedule()untuk memilih proses baru untuk dijalankan. Perhatikan, SIGSTOPtidak dapat ditangkap (seperti SIGKILL), oleh karena itu tidak ada kemungkinan untuk mendaftarkan penangan sinyal manual. Jika ada sinyal yang tidak dapat ditonton, tindakan default akan selalu dijalankan.


Untuk pertanyaan Anda:

Proses yang mati atau mati tidak akan pernah ditentukan oleh penjadwal untuk berada dalam TASK_RUNNINGstatus lagi. Dengan demikian kernel tidak akan pernah menjalankan penangan sinyal (default atau didefinisikan) untuk sinyal yang sesuai, sinyal mana pun itu. Oleh karena itu exit_signaltidak akan pernah diatur lagi. Sinyal "disampaikan" untuk proses dengan menetapkan signal_structdalam task_structproses, tapi tidak ada yang lain akan terjadi, karena proses tidak akan berjalan lagi. Tidak ada kode untuk dijalankan, semua yang tersisa dari proses adalah struct proses itu.

Namun, jika proses induk menuai anaknya wait(), kode keluar yang diterimanya adalah ketika proses "awalnya" mati. Tidak masalah jika ada sinyal yang menunggu untuk ditangani.

kekacauan
sumber
Ya, tetapi apakah perintah killitu sendiri mengembalikan 0 atau 1?
Anthony Rutledge
9

Proses zombie pada dasarnya sudah mati. Satu-satunya hal adalah bahwa belum ada yang mengakui kematiannya sehingga ia terus menempati entri dalam tabel proses serta blok kontrol (struktur yang dipertahankan oleh kernel Linux untuk setiap utas dalam aktivitas). Sumber daya lain seperti kunci wajib pada file, segmen memori bersama, semaphore, dll. Direklamasi.

Anda tidak dapat memberi sinyal kepada mereka karena tidak ada yang dapat bertindak atas sinyal ini. Bahkan sinyal fatal seperti KILL tidak berguna karena prosesnya telah menghentikan eksekusi. Anda dapat mencoba sendiri:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
    pid_t pid = fork();

    if (pid == -1)
        exit(-1);

    if (pid > 0) {
        //parent
        printf("[parent]: I'm the parent, the pid of my child is %i\n"
            "I'll start waiting for it in 10 seconds.\n", pid);
        sleep(10);
        int status;
        wait(&status);

        if (WIFSIGNALED(status)) {
            printf("[parent]: My child has died from a signal: %i\n", WTERMSIG(status));
        } else if (WIFEXITED(status)) {
            printf("[parent]: My child has died from natural death\n");
        } else {
            printf("[parent]: I don't know what happened to my child\n");
        }
    } else {
        //child
        printf("[child]: I'm dying soon, try to kill me.\n");
        sleep(5);
        printf("[child]: Dying now!\n");
    }

    return 0;
}

Di sini, saya memulai proses yang bercabang dan tidur sebelum menunggu anaknya. Anak itu tidak melakukan apa pun kecuali tidur sedikit. Anda dapat membunuh anak ketika sedang tidur atau tepat setelah keluar untuk melihat perbedaannya:

$ make zombie 
cc     zombie.c   -o zombie

$ ./zombie    
[parent]: I'm the parent, the pid of my child is 16693
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
# Here, I did "kill -15 16693" in another console
[parent]: My child has died from a signal: 15

$ ./zombie
[parent]: I'm the parent, the pid of my child is 16717
I'll start waiting for it in 10 seconds.
[child]: I'm dying soon, try to kill me.
[child]: Dying now!
# Here, I did "kill -15 16717" in another console
[parent]: My child has died from natural death
lororget
sumber
Saya ingin menemukan bagian yang relevan dalam kode sumber kernel untuk Anda, tetapi saya berjuang untuk menemukannya ...
lgeorget
@Igeorget Terima kasih tapi tidak apa-apa, saya tidak perlu melihat kode kernel.
user137481
Program Ruby sederhana yang melakukan proses, dan anak itu segera keluar ruby -e "loop while fork { exit! }"... imgur.com/SoRXErm
S.Goswami