Bagaimana sinyal bekerja secara internal?

31

Secara umum, untuk mematikan proses kami menghasilkan sinyal seperti SIGKILL, SIGTSTPdll.

Tetapi bagaimana diketahui siapa yang memerintahkan sinyal tertentu, siapa yang mengirimnya ke proses tertentu, dan secara umum bagaimana sinyal melakukan operasi mereka? Bagaimana cara kerja sinyal secara internal?

Varun Chhangani
sumber
Pertanyaannya agak sulit untuk dipahami. Saya minta maaf dan berarti tidak hormat. Apakah Anda ingin tahu siapa yang menjalankan perintah yang mematikan proses atau Anda ingin tahu lebih banyak tentang SIGKILL dan SIGSTP?
pullsumo
@ tuan saya ingin tahu siapa yang mungkin menjalankan perintah yang membunuh proses dan bagaimana?
Varun Chhangani

Jawaban:

35

Tampilan 50.000 kaki adalah:

  1. Sinyal dihasilkan oleh kernel secara internal (misalnya, SIGSEGVketika alamat yang tidak valid diakses, atau SIGQUITketika Anda menekan Ctrl+ \), atau oleh program yang menggunakan killsyscall (atau beberapa yang terkait).

  2. Jika melalui salah satu syscalls, maka kernel mengkonfirmasi proses pemanggilan memiliki hak yang cukup untuk mengirim sinyal. Jika tidak, kesalahan dikembalikan (dan sinyal tidak terjadi).

  3. Jika itu adalah salah satu dari dua sinyal khusus, kernel bekerja tanpa syarat di dalamnya, tanpa input dari proses target. Dua sinyal khusus adalah SIGKILL dan SIGSTOP. Semua hal di bawah ini tentang tindakan default, memblokir sinyal, dll., Tidak relevan untuk keduanya.

  4. Selanjutnya, kernel mengetahui apa yang harus dilakukan dengan sinyal:

    1. Untuk setiap proses, ada tindakan yang terkait dengan setiap sinyal. Ada banyak default, dan program dapat mengatur yang berbeda menggunakan sigaction,, signaldll. Ini termasuk hal-hal seperti "abaikan sepenuhnya", "matikan proses", "matikan proses dengan dump inti", "hentikan proses", dll.

    2. Program juga dapat mematikan pengiriman sinyal ("diblokir"), atas dasar sinyal demi sinyal. Kemudian sinyal tetap tertunda hingga tidak terblokir.

    3. Program dapat meminta bahwa alih-alih kernel mengambil beberapa tindakan itu sendiri, ia mengirimkan sinyal ke proses baik secara sinkron (dengan sigwait, et. Al. Atau signalfd) atau secara tidak sinkron (dengan menyela apa pun yang sedang dilakukan proses, dan memanggil fungsi yang ditentukan).

Ada seperangkat sinyal kedua yang disebut "sinyal waktu-nyata", yang tidak memiliki arti khusus, dan juga memungkinkan beberapa sinyal untuk antri (sinyal normal hanya akan mengantri satu dari masing-masing ketika sinyal diblokir). Ini digunakan dalam program multi-utas untuk utas untuk berkomunikasi satu sama lain. Beberapa digunakan dalam implementasi utas POSIX glibc, misalnya. Mereka juga dapat digunakan untuk berkomunikasi antara proses yang berbeda (misalnya, Anda dapat menggunakan beberapa sinyal real-time untuk meminta program fooctl mengirim pesan ke daemon foo).

Untuk tampilan non-50.000 kaki, cobalah man 7 signaldan juga dokumentasi internal kernel (atau sumber).

derobert
sumber
"Dua sinyal khusus adalah SIGKILL dan SIGSTOP" jadi apa yang mungkin menjadi SIGCONT ...
Hauke ​​Laging
@HaukeLaging SIGCONT adalah sinyal yang membatalkan SIGSTOP. Dokumentasi tidak mencantumkannya sebagai istimewa ... Jadi saya tidak yakin jika secara teknis suatu proses dapat mengaturnya untuk diabaikan, maka Anda tidak akan dapat melanjutkannya (hanya SIGKILL saja).
derobert
22

Implementasi sinyal sangat kompleks dan spesifik untuk kernel. Dengan kata lain, kernel yang berbeda akan mengimplementasikan sinyal secara berbeda. Penjelasan yang disederhanakan adalah sebagai berikut:

CPU, berdasarkan pada nilai register khusus, memiliki alamat di memori di mana ia mengharapkan untuk menemukan "tabel deskriptor interupsi" yang sebenarnya merupakan tabel vektor. Ada satu vektor untuk setiap pengecualian yang mungkin, seperti pembagian dengan nol, atau perangkap, seperti INT 3 (debug). Ketika CPU menemukan pengecualian, ia menyimpan flag dan pointer instruksi saat ini di stack dan kemudian melompat ke alamat yang ditentukan oleh vektor yang relevan. Di Linux vektor ini selalu menunjuk ke kernel, di mana ada handler pengecualian. CPU sekarang selesai, dan kernel Linux mengambil alih.

Perhatikan, bahwa Anda juga dapat memicu pengecualian dari perangkat lunak. Misalnya, pengguna menekan CTRL- C, maka panggilan ini pergi ke kernel yang memanggil pengendali pengecualiannya sendiri. Secara umum, ada beberapa cara berbeda untuk sampai ke handler, tetapi terlepas dari hal dasar yang sama terjadi: konteksnya disimpan di stack dan handler pengecualian kernel dilompati.

Pawang pengecualian kemudian memutuskan utas apa yang harus menerima sinyal. Jika sesuatu seperti pembagian-oleh-nol terjadi, maka itu mudah: utas yang menyebabkan perkecualian mendapatkan sinyal, tetapi untuk jenis sinyal lainnya, keputusannya bisa sangat kompleks dan dalam beberapa kasus yang tidak biasa, untaian acak kurang lebih mungkin dapatkan sinyalnya.

Untuk mengirim sinyal apa yang dilakukan oleh kernel adalah pertama-tama mengatur nilai yang menunjukkan jenis sinyal, SIGHUPatau apa pun. Ini hanya bilangan bulat. Setiap proses memiliki area memori "pending signal" di mana nilai ini disimpan. Kemudian kernel membuat struktur data dengan informasi sinyal. Struktur ini termasuk sinyal "disposisi" yang mungkin default, abaikan atau tangani. Kernel kemudian memanggil fungsinya sendiri do_signal(). Fase selanjutnya dimulai.

do_signal()pertama memutuskan apakah itu akan menangani sinyal. Misalnya, jika itu adalah pembunuhan , maka do_signal()cukup bunuh prosesnya, akhir cerita. Kalau tidak, itu terlihat pada disposisi. Jika disposisi default, maka do_signal()menangani sinyal sesuai dengan kebijakan standar yang tergantung pada sinyal. Jika disposisi diatur, maka itu berarti ada fungsi dalam program pengguna yang dirancang untuk menangani sinyal yang dimaksud dan penunjuk ke fungsi ini akan berada dalam struktur data yang disebutkan di atas. Dalam hal ini do_signal () memanggil fungsi kernel lain,handle_signal(), yang kemudian melewati proses beralih kembali ke mode pengguna dan memanggil fungsi ini. Detail dari handoff ini sangat kompleks. Kode ini dalam program Anda biasanya ditautkan secara otomatis ke dalam program Anda ketika Anda menggunakan fungsi-fungsi di signal.h.

Dengan memeriksa nilai sinyal yang tertunda dengan tepat, kernel dapat menentukan apakah proses tersebut menangani semua sinyal, dan akan mengambil tindakan yang tepat jika tidak, yang mungkin menyebabkan proses tidur atau membunuhnya atau tindakan lain, tergantung pada sinyalnya.

Tyler Durden
sumber
15

Meskipun pertanyaan ini telah dijawab, izinkan saya memposting alur acara yang terperinci dalam kernel Linux.
Ini sepenuhnya disalin dari posting Linux: Linux Signals - Internal di blog "Linux posts" di sklinuxblog.blogspot.in.

Program Ruang Pengguna Sinyal C

Mari kita mulai dengan menulis program ruang C pengguna sinyal sederhana:

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

/* Handler function */
void handler(int sig) {
    printf("Receive signal: %u\n", sig);
};

int main(void) {
    struct sigaction sig_a;

    /* Initialize the signal handler structure */
    sig_a.sa_handler = handler;
    sigemptyset(&sig_a.sa_mask);
    sig_a.sa_flags = 0;

    /* Assign a new handler function to the SIGINT signal */
    sigaction(SIGINT, &sig_a, NULL);

    /* Block and wait until a signal arrives */
    while (1) {
            sigsuspend(&sig_a.sa_mask);
            printf("loop\n");
    }
    return 0;
};

Kode ini memberikan penangan baru untuk sinyal SIGINT. SIGINT dapat dikirim ke proses yang berjalan menggunakan kombinasi tombol Ctrl+ C. Ketika Ctrl+ Cditekan maka sinyal asinkron SIGINT dikirim ke tugas. Ini juga setara dengan mengirim kill -INT <pid>perintah di terminal lain.

Jika Anda melakukan kill -l(itu huruf kecil L, yang berarti "daftar"), Anda akan mengetahui berbagai sinyal yang dapat dikirim ke proses yang sedang berjalan.

[root@linux ~]# kill -l
 1) SIGHUP        2) SIGINT        3) SIGQUIT       4) SIGILL        5) SIGTRAP
 6) SIGABRT       7) SIGBUS        8) SIGFPE        9) SIGKILL      10) SIGUSR1
11) SIGSEGV      12) SIGUSR2      13) SIGPIPE      14) SIGALRM      15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD      18) SIGCONT      19) SIGSTOP      20) SIGTSTP
21) SIGTTIN      22) SIGTTOU      23) SIGURG       24) SIGXCPU      25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF      28) SIGWINCH     29) SIGIO        30) SIGPWR
31) SIGSYS       34) SIGRTMIN     35) SIGRTMIN+1   36) SIGRTMIN+2   37) SIGRTMIN+3
38) SIGRTMIN+4   39) SIGRTMIN+5   40) SIGRTMIN+6   41) SIGRTMIN+7   42) SIGRTMIN+8
43) SIGRTMIN+9   44) SIGRTMIN+10  45) SIGRTMIN+11  46) SIGRTMIN+12  47) SIGRTMIN+13
48) SIGRTMIN+14  49) SIGRTMIN+15  50) SIGRTMAX-14  51) SIGRTMAX-13  52) SIGRTMAX-12
53) SIGRTMAX-11  54) SIGRTMAX-10  55) SIGRTMAX-9   56) SIGRTMAX-8   57) SIGRTMAX-7
58) SIGRTMAX-6   59) SIGRTMAX-5   60) SIGRTMAX-4   61) SIGRTMAX-3   62) SIGRTMAX-2
63) SIGRTMAX-1   64) SIGRTMAX

Juga kombinasi tombol berikut dapat digunakan untuk mengirim sinyal tertentu:

  • Ctrl+ C- mengirimkan SIGINT tindakan default mana yang akan menghentikan aplikasi.
  • Ctrl+ \  - mengirimkan SIGQUIT yang merupakan tindakan default untuk menghentikan inti dumping aplikasi.
  • Ctrl+ Z- mengirimkan SIGSTOP yang menghentikan program.

Jika Anda mengkompilasi dan menjalankan program C di atas maka Anda akan mendapatkan output berikut:

[root@linux signal]# ./a.out
Receive signal: 2
loop
Receive signal: 2
loop
^CReceive signal: 2
loop

Bahkan dengan Ctrl+ Catau kill -2 <pid>proses tidak akan berakhir. Sebaliknya itu akan menjalankan penangan sinyal dan kembali.

Bagaimana sinyal dikirim ke proses

Jika kita melihat internal pengiriman sinyal ke suatu proses dan menempatkan Jprobe dengan dump_stack di __send_signalfungsi kita akan melihat jejak panggilan berikut:

May  5 16:18:37 linux kernel: dump_stack+0x19/0x1b
May  5 16:18:37 linux kernel: my_handler+0x29/0x30 (probe)
May  5 16:18:37 linux kernel: complete_signal+0x205/0x250
May  5 16:18:37 linux kernel: __send_signal+0x194/0x4b0
May  5 16:18:37 linux kernel: send_signal+0x3e/0x80
May  5 16:18:37 linux kernel: do_send_sig_info+0x52/0xa0
May  5 16:18:37 linux kernel: group_send_sig_info+0x46/0x50
May  5 16:18:37 linux kernel: __kill_pgrp_info+0x4d/0x80
May  5 16:18:37 linux kernel: kill_pgrp+0x35/0x50
May  5 16:18:37 linux kernel: n_tty_receive_char+0x42b/0xe30
May  5 16:18:37 linux kernel:  ? ftrace_ops_list_func+0x106/0x120
May  5 16:18:37 linux kernel: n_tty_receive_buf+0x1ac/0x470
May  5 16:18:37 linux kernel: flush_to_ldisc+0x109/0x160
May  5 16:18:37 linux kernel: process_one_work+0x17b/0x460
May  5 16:18:37 linux kernel: worker_thread+0x11b/0x400
May  5 16:18:37 linux kernel: rescuer_thread+0x400/0x400
May  5 16:18:37 linux kernel:  kthread+0xcf/0xe0
May  5 16:18:37 linux kernel:  kthread_create_on_node+0x140/0x140
May  5 16:18:37 linux kernel:  ret_from_fork+0x7c/0xb0
May  5 16:18:37 linux kernel: ? kthread_create_on_node+0x140/0x140

Jadi fungsi utama panggilan untuk mengirim sinyal seperti:

First shell send the Ctrl+C signal using n_tty_receive_char
n_tty_receive_char()
isig()
kill_pgrp()
__kill_pgrp_info()
group_send_sig_info() -- for each PID in group call this function
do_send_sig_info()
send_signal()
__send_signal() -- allocates a signal structure and add to task pending signals
complete_signal()
signal_wake_up()
signal_wake_up_state()  -- sets TIF_SIGPENDING in the task_struct flags. Then it wake up the thread to which signal was delivered.

Sekarang semuanya sudah diatur dan perubahan yang diperlukan dilakukan untuk task_structproses.

Penanganan sinyal

Sinyal diperiksa / ditangani oleh suatu proses ketika kembali dari panggilan sistem atau jika kembali dari interupsi dilakukan. Pengembalian dari panggilan sistem hadir dalam file entry_64.S.

Fungsi int_signal function dipanggil dari entry_64.Smana memanggil fungsi do_notify_resume().

Mari kita periksa fungsinya do_notify_resume(). Fungsi ini memeriksa apakah kita TIF_SIGPENDINGmenetapkan flag di task_struct:

 /* deal with pending signal delivery */
 if (thread_info_flags & _TIF_SIGPENDING)
  do_signal(regs);
do_signal calls handle_signal to call the signal specific handler
Signals are actually run in user mode in function:
__setup_rt_frame -- this sets up the instruction pointer to handler: regs->ip = (unsigned long) ksig->ka.sa.sa_handler;

Panggilan dan sinyal SISTEM

Syscalls "Lambat" misalnya memblokir baca / tulis, menempatkan proses ke status menunggu: TASK_INTERRUPTIBLEatau TASK_UNINTERRUPTIBLE.

Tugas dalam status TASK_INTERRUPTIBLEakan diubah ke TASK_RUNNINGstatus oleh sinyal. TASK_RUNNINGberarti suatu proses dapat dijadwalkan.

Jika dijalankan, penangan sinyalnya akan dijalankan sebelum penyelesaian syscall "lambat". Tidak syscalllengkap secara default.

Jika SA_RESTARTflag diatur, syscalldihidupkan ulang setelah penangan sinyal selesai.

Referensi

K_K
sumber
Terima kasih telah berupaya berkontribusi ke situs ini, tetapi (1) jika Anda akan menyalin materi dari situs lain (kata demi kata, surat untuk surat, termasuk kesalahan tata bahasa dan tanda baca), Anda harus mengatakan bahwa Anda sedang melakukan jadi, jauh lebih jelas. Mendaftarkan sumber sebagai "Referensi", meskipun perlu, tidak cukup. Kecuali jika Anda adalah penulis blog (K_K = sk?), Dalam hal ini Anda tidak diharuskan untuk menautkannya - tetapi, jika Anda melakukannya, Anda harus mengungkapkan (mis., Katakan) bahwa itu adalah milik Anda. … (Lanjutan)
G-Man Mengatakan 'Reinstate Monica'
(Lanjutan) ... (2) Sumber Anda (blog yang Anda salin) tidak terlalu bagus. Sudah empat tahun sejak pertanyaan diajukan; Anda tidak dapat menemukan referensi yang lebih baik untuk menyalin? (Jika Anda adalah penulis asli, maaf.) Selain kesalahan tata bahasa dan tanda baca yang disebutkan di atas (dan kata-kata umumnya ceroboh, dan format buruk), itu salah. (2a) Ctrl + Z mengirim SIGTSTP, bukan SIGSTOP. (SIGTSTP, seperti SIGTERM, dapat ditangkap; SIGSTOP, seperti SIGKILL, tidak bisa.)… (Lanjutan)
G-Man Mengatakan 'Reinstate Monica'
(Lanjutan) ... (2b) Shell tidak mengirim sinyal Ctrl + C. Shell tidak memiliki peran dalam mengirimkan sinyal (kecuali ketika pengguna menggunakan killperintah, yang merupakan shell builtin). (2c) Meskipun titik koma setelah penutupan }suatu fungsi bukanlah kesalahan, mereka tidak perlu dan sangat tidak ortodoks. (3) Bahkan jika semuanya benar, itu tidak akan menjadi jawaban yang sangat baik untuk pertanyaan itu. (3a) Pertanyaannya, meskipun agak tidak jelas, tampaknya berfokus pada bagaimana aktor (pengguna dan proses) memulai (yaitu, mengirim ) sinyal. … (Lanjutan)
G-Man Mengatakan 'Reinstate Monica'
(Lanjutan) ... Jawabannya tampaknya berfokus pada sinyal yang dihasilkan oleh kernel (khususnya, sinyal yang dihasilkan keyboard) dan bagaimana proses penerima bereaksi terhadap sinyal. (3b) Pertanyaannya tampaknya berada pada tingkat «Seseorang membunuh proses saya - siapa yang melakukannya, dan bagaimana?» Jawabannya membahas API penanganan sinyal, rutinitas kernel, debugging kernel (Jprobe?), Jejak tumpukan kernel, dan struktur data kernel. IMO, itu adalah level rendah yang tidak tepat - terutama karena tidak memberikan referensi di mana pembaca dapat lebih belajar tentang cara kerja internal ini.
G-Man Mengatakan 'Reinstate Monica'
1
Ini adalah blog saya sendiri .. jejak saya sendiri .. itu yang saya inginkan .. semua orang akan tahu aliran terperinci seperti itu .. berbicara di udara tidak masuk akal .. bahkan jika setelah itu melanggar pedoman komunitas ini, harap jawaban saya dihapus dengan tepat saluran .. ini adalah jawaban internal kernel bukan tata bahasa internal.
K_K