สัญญาณทำงานอย่างไรภายใน


31

โดยทั่วไปจะฆ่ากระบวนการที่เราสร้างสัญญาณเช่นSIGKILL, SIGTSTPฯลฯ

แต่จะทราบได้อย่างไรว่าใครสั่งให้สัญญาณนั้นใครส่งไปยังกระบวนการเฉพาะและโดยทั่วไปสัญญาณทำงานอย่างไร สัญญาณภายในทำงานอย่างไร


คำถามนี้ยากที่จะเข้าใจ ฉันขอโทษและหมายถึงไม่ดูหมิ่น คุณต้องการที่จะรู้ว่าใครอาจมีคำสั่งที่ฆ่ากระบวนการหรือคุณต้องการที่จะรู้เพิ่มเติมเกี่ยวกับ SIGKILL และ SIGSTP?
pullsumo

@mistermister ฉันต้องการที่จะรู้ว่าใครอาจได้วิ่งคำสั่งที่ฆ่ากระบวนการและอย่างไร
Varun Chhangani

คำตอบ:


35

มุมมอง 50,000 ฟุตคือ:

  1. สัญญาณถูกสร้างขึ้นโดยเคอร์เนลภายใน (ตัวอย่างเช่นSIGSEGVเมื่อมีการเข้าถึงที่อยู่ที่ไม่ถูกต้องหรือSIGQUITเมื่อคุณกดCtrl+ \) หรือโดยโปรแกรมที่ใช้killsyscall (หรือหลายรายการที่เกี่ยวข้อง)

  2. หากเป็นหนึ่งใน syscalls แสดงว่าเคอร์เนลยืนยันว่ากระบวนการเรียกมีสิทธิ์เพียงพอที่จะส่งสัญญาณ หากไม่มีข้อผิดพลาดจะถูกส่งกลับ (และสัญญาณจะไม่เกิดขึ้น)

  3. หากเป็นสัญญาณพิเศษหนึ่งในสองเคอร์เนลจะทำงานโดยไม่มีเงื่อนไขโดยไม่มีอินพุตจากกระบวนการเป้าหมาย สัญญาณพิเศษสองสัญญาณคือ SIGKILL และ SIGSTOP ทุกสิ่งด้านล่างเกี่ยวกับการกระทำเริ่มต้นการปิดกั้นสัญญาณ ฯลฯ ไม่เกี่ยวข้องกับสิ่งเหล่านี้

  4. ถัดไปเคอร์เนลจะหาว่ามีสัญญาณอะไร:

    1. สำหรับแต่ละกระบวนการมีการดำเนินการที่เกี่ยวข้องกับแต่ละสัญญาณ มีพวงของการเริ่มต้นเป็นและโปรแกรมสามารถตั้งคนที่แตกต่างโดยใช้sigaction, signalฯลฯ เหล่านี้รวมถึงสิ่งที่ชอบ "ไม่สนใจมันสมบูรณ์", "ฆ่ากระบวนการ", "ฆ่ากระบวนการที่มีการถ่ายโอนข้อมูลหลัก", "หยุดกระบวนการ" เป็นต้น

    2. โปรแกรมสามารถปิดการส่งสัญญาณ ("ถูกบล็อก") โดยใช้สัญญาณต่อสัญญาณ จากนั้นสัญญาณจะยังคงค้างอยู่จนกว่าจะไม่ถูกบล็อก

    3. โปรแกรมสามารถร้องขอให้แทนที่เคอร์เนลที่ดำเนินการบางอย่างเองมันส่งสัญญาณไปยังกระบวนการทั้งแบบซิงโครนัส (กับsigwait, et. al. หรือsignalfd) หรือแบบอะซิงโครนัส (โดยการขัดจังหวะกระบวนการที่ทำ

มีสัญญาณชุดที่สองที่เรียกว่า "สัญญาณเรียลไทม์" ซึ่งไม่มีความหมายเฉพาะและอนุญาตให้มีสัญญาณหลายรายการที่จะเข้าคิว (สัญญาณคิวปกติเพียงหนึ่งสัญญาณเท่านั้นเมื่อสัญญาณถูกบล็อก) สิ่งเหล่านี้ถูกใช้ในโปรแกรมแบบมัลติเธรดเพื่อให้เธรดสามารถสื่อสารกันได้ ตัวอย่างจำนวนมากถูกใช้ในการใช้เธรด POSIX ของ glibc ตัวอย่างเช่น พวกเขายังสามารถใช้ในการสื่อสารระหว่างกระบวนการต่าง ๆ (ตัวอย่างเช่นคุณสามารถใช้สัญญาณเรียลไทม์จำนวนมากเพื่อให้โปรแกรม fooctl ส่งข้อความไปยัง foo daemon)

สำหรับมุมมองที่ไม่ใช่ 50,000 ลองman 7 signalและเอกสารประกอบเคอร์เนล internals (หรือแหล่งที่มา)


"สัญญาณพิเศษสองสัญญาณคือ SIGKILL และ SIGSTOP" ดังนั้น SIGCONT อาจจะเป็น ...
Hauke ​​Laging

@HaukeLaging SIGCONT เป็นสัญญาณที่ยกเลิก SIGSTOP เอกสารไม่ได้ระบุว่าเป็นพิเศษ ... ดังนั้นฉันไม่แน่ใจว่าในทางเทคนิคแล้วกระบวนการสามารถตั้งค่าให้เพิกเฉยได้หรือไม่คุณไม่สามารถดำเนินการต่อได้ (เฉพาะ SIGKILL)
Derobert

22

การปรับใช้สัญญาณมีความซับซ้อนมากและมีความเฉพาะเจาะจงของเคอร์เนล ในคำอื่น ๆ เมล็ดที่แตกต่างกันจะใช้สัญญาณต่างกัน คำอธิบายที่ง่ายขึ้นมีดังนี้:

CPU ขึ้นอยู่กับค่าลงทะเบียนพิเศษมีที่อยู่ในหน่วยความจำซึ่งคาดว่าจะพบ "ตารางตัวอธิบายขัดจังหวะ" ซึ่งจริงๆแล้วเป็นตารางเวกเตอร์ มีเวกเตอร์เดียวสำหรับทุกข้อยกเว้นที่เป็นไปได้เช่นการหารด้วยศูนย์หรือกับดักเช่น INT 3 (ดีบั๊ก) เมื่อ CPU พบข้อยกเว้นมันจะบันทึกสถานะและตัวชี้คำสั่งปัจจุบันบนสแต็กจากนั้นข้ามไปยังที่อยู่ที่ระบุโดยเวกเตอร์ที่เกี่ยวข้อง ใน Linux เวกเตอร์นี้ชี้ไปที่เคอร์เนลเสมอซึ่งมีตัวจัดการข้อยกเว้น CPU เสร็จสิ้นแล้วและเคอร์เนล Linux จะเข้าแทนที่

โปรดทราบว่าคุณยังสามารถทริกเกอร์ข้อยกเว้นจากซอฟต์แวร์ ตัวอย่างเช่นผู้ใช้กดCTRL- Cจากนั้นการโทรนี้จะไปที่เคอร์เนลซึ่งเรียกตัวจัดการข้อยกเว้นของตัวเอง โดยทั่วไปมีวิธีที่แตกต่างกันในการเข้าถึงตัวจัดการ แต่ไม่ว่าจะเกิดอะไรขึ้นกับพื้นฐาน: บริบทจะถูกบันทึกไว้ในสแต็กและตัวจัดการข้อยกเว้นของเคอร์เนลจะถูกข้ามไป

ตัวจัดการข้อยกเว้นจะตัดสินใจว่าเธรดใดควรรับสัญญาณ หากสิ่งที่ต้องการแบ่งเป็นศูนย์เกิดขึ้นมันเป็นเรื่องง่าย: เธรดที่ทำให้เกิดข้อยกเว้นได้รับสัญญาณ แต่สำหรับสัญญาณประเภทอื่นการตัดสินใจอาจซับซ้อนมากและในบางกรณีที่ผิดปกติอาจมีเธรดสุ่มมากหรือน้อยก็ได้ รับสัญญาณ

ในการส่งสัญญาณสิ่งที่เคอร์เนลทำคือตั้งค่าที่ระบุประเภทของสัญญาณSIGHUPหรืออะไรก็ตาม นี่เป็นเพียงจำนวนเต็ม ทุกกระบวนการมีพื้นที่หน่วยความจำ "รอสัญญาณ" ที่เก็บค่านี้ จากนั้นเคอร์เนลจะสร้างโครงสร้างข้อมูลพร้อมกับข้อมูลสัญญาณ โครงสร้างนี้รวมถึงสัญญาณ "การจัดการ" ซึ่งอาจเป็นค่าเริ่มต้นละเว้นหรือจัดการ do_signal()เคอร์เนลแล้วเรียกฟังก์ชั่นของตัวเอง ระยะต่อไปจะเริ่มขึ้น

do_signal()ครั้งแรกที่ตัดสินใจว่ามันจะจัดการสัญญาณ ตัวอย่างเช่นถ้าเป็นการฆ่าก็do_signal()แค่ฆ่ากระบวนการจบเรื่อง มิฉะนั้นจะดูที่การจำหน่าย หากการจัดการเป็นค่าเริ่มต้นแล้วdo_signal()จัดการสัญญาณตามนโยบายเริ่มต้นที่ขึ้นอยู่กับสัญญาณ หากการจัดการมีการจัดการก็หมายความว่ามีฟังก์ชั่นในโปรแกรมผู้ใช้ที่ถูกออกแบบมาเพื่อจัดการกับสัญญาณที่เป็นปัญหาและตัวชี้ไปยังฟังก์ชั่นนี้จะอยู่ในโครงสร้างข้อมูลดังกล่าวข้างต้น ในกรณีนี้ do_signal () เรียกใช้ฟังก์ชันเคอร์เนลอื่นhandle_signal()ซึ่งจะผ่านกระบวนการสลับกลับไปเป็นโหมดผู้ใช้และเรียกใช้ฟังก์ชันนี้ รายละเอียดของการแฮนด์ออฟนี้มีความซับซ้อนมาก signal.hรหัสในโปรแกรมของคุณนี้มักจะมีการเชื่อมโยงโดยอัตโนมัติในโปรแกรมของคุณเมื่อคุณใช้ฟังก์ชั่นใน

โดยการตรวจสอบค่าสัญญาณที่รอดำเนินการอย่างเหมาะสมเคอร์เนลสามารถกำหนดได้ว่ากระบวนการจัดการสัญญาณทั้งหมดหรือไม่และจะดำเนินการตามความเหมาะสมหากไม่เป็นเช่นนั้นซึ่งอาจทำให้กระบวนการเข้าสู่โหมดสลีปหรือฆ่าหรือดำเนินการอื่น ๆ ขึ้นอยู่กับสัญญาณ


15

แม้ว่าคำถามนี้จะได้รับคำตอบให้ฉันโพสต์รายละเอียดไหลของเหตุการณ์ใน Linux kernel
สิ่งนี้จะถูกคัดลอกมาจากกระทู้ Linux: สัญญาณ Linux - ภายใน ที่บล็อก“ โพสต์ Linux” ที่ sklinuxblog.blogspot.in

โปรแกรมผู้ใช้พื้นที่สัญญาณ C

เริ่มต้นด้วยการเขียนโปรแกรม C พื้นที่ผู้ใช้สัญญาณอย่างง่าย:

#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;
};

รหัสนี้กำหนดตัวจัดการใหม่สำหรับสัญญาณ SIGINT SIGINT สามารถถูกส่งไปยังกระบวนการที่ทำงานโดยใช้Ctrl+ Cชุดคีย์ เมื่อกดCtrl+ Cสัญญาณอะซิงโครนัส SIGINT จะถูกส่งไปยังงาน นอกจากนี้ยังเทียบเท่ากับการส่งkill -INT <pid>คำสั่งในเทอร์มินัลอื่น

หากคุณทำkill -l(นั่นคือตัวพิมพ์เล็กLซึ่งหมายถึง "รายการ") คุณจะได้รู้สัญญาณต่าง ๆ ที่สามารถส่งไปยังกระบวนการที่กำลังทำงานอยู่

[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

การผสมคีย์ต่อไปนี้สามารถใช้ในการส่งสัญญาณโดยเฉพาะ:

  • Ctrl+ C- ส่ง SIGINT ซึ่งการกระทำเริ่มต้นคือการยกเลิกแอปพลิเคชัน
  • Ctrl+ \  - ส่ง SIGQUIT ซึ่งการกระทำเริ่มต้นคือการยกเลิกแอปพลิเคชันหลักทิ้ง
  • Ctrl+ Z- ส่ง SIGSTOP ที่หยุดโปรแกรมชั่วคราว

หากคุณรวบรวมและเรียกใช้โปรแกรม C ข้างต้นแล้วคุณจะได้รับผลลัพธ์ต่อไปนี้:

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

แม้จะมีCtrl+ Cหรือkill -2 <pid>กระบวนการจะไม่ยุติ แต่มันจะดำเนินการจัดการสัญญาณและกลับมา

วิธีการส่งสัญญาณไปยังกระบวนการ

หากเราเห็น internals ของสัญญาณที่ส่งไปยังกระบวนการและใส่ Jprobe กับ dump_stack ที่__send_signalฟังก์ชันเราจะเห็นการติดตามการโทร:

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

ดังนั้นฟังก์ชั่นหลักเรียกร้องให้มีการส่งสัญญาณเช่น:

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.

ตอนนี้ทุกอย่างได้รับการตั้งค่าและการเปลี่ยนแปลงที่จำเป็นจะเกิดขึ้นกับtask_structกระบวนการ

การจัดการสัญญาณ

สัญญาณจะถูกตรวจสอบ / จัดการโดยกระบวนการเมื่อมันกลับมาจากการเรียกของระบบหรือถ้าการส่งคืนจากการขัดจังหวะเสร็จสิ้น entry_64.Sผลตอบแทนจากการเรียกระบบมีอยู่ในไฟล์

ฟังก์ชั่นการทำงาน int_signal ถูกเรียกจากที่เรียกฟังก์ชั่นentry_64.Sdo_notify_resume()

do_notify_resume()ขอตรวจสอบการทำงาน ฟังก์ชันนี้ตรวจสอบว่าเรามีการTIF_SIGPENDINGตั้งค่าสถานะใน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;

สายระบบและสัญญาณ

“ช้า” syscalls เช่นการปิดกั้นการอ่าน / เขียนใส่เข้าไปในกระบวนการรอรัฐ: หรือTASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE

งานในสถานะTASK_INTERRUPTIBLEจะถูกเปลี่ยนเป็นTASK_RUNNINGสถานะโดยสัญญาณ TASK_RUNNINGหมายถึงกระบวนการสามารถกำหนด

หากดำเนินการแล้วตัวจัดการสัญญาณของมันจะถูกเรียกใช้ก่อนที่จะเสร็จ syscall "ช้า" syscallไม่สมบูรณ์โดยค่าเริ่มต้น

หากSA_RESTARTตั้งค่าสถานะsyscallจะเริ่มต้นใหม่หลังจากตัวจัดการสัญญาณเสร็จสิ้น

อ้างอิง


ขอขอบคุณที่พยายามทุ่มเทให้กับเว็บไซต์ แต่ (1) หากคุณกำลังจะคัดลอกเนื้อหาจากเว็บไซต์อื่น (คำต่อคำจดหมายสำหรับตัวอักษรรวมถึงข้อผิดพลาดทางไวยากรณ์และเครื่องหมายวรรคตอน) คุณควรจะบอกว่าคุณกำลังทำ ดังนั้นชัดเจนมากขึ้น การระบุแหล่งที่มาว่าเป็น "ข้อมูลอ้างอิง" ในขณะที่จำเป็นยังไม่เพียงพอ นอกจากคุณจะเป็นผู้เขียนบล็อก (K_K = sk?) ซึ่งในกรณีนี้คุณไม่จำเป็นต้องเชื่อมโยงกับบล็อก แต่ถ้าคุณทำคุณต้องเปิดเผย (เช่นพูด) ว่าเป็นของคุณ … (ต่อ)
G-Man พูดว่า 'Reinstate Monica'

(ต่อ) ... (2) แหล่งที่มาของคุณ (บล็อกที่คุณคัดลอกมา) ไม่ดีมาก สี่ปีแล้วที่คำถามถูกถาม คุณไม่พบการอ้างอิงที่ดีกว่าในการคัดลอกจากหรือ (หากคุณเป็นผู้เขียนต้นฉบับขออภัย) นอกจากข้อผิดพลาดด้านไวยากรณ์และเครื่องหมายวรรคตอนข้างต้น (และโดยทั่วไปแล้วถ้อยคำเลอะเทอะและการจัดรูปแบบไม่ดี) มันผิด (2a) Ctrl + Z ส่ง SIGTSTP ไม่ใช่ SIGSTOP (SIGTSTP เช่น SIGTERM สามารถถูกจับได้ SIGSTOP เหมือน SIGKILL ไม่สามารถทำได้) … (ต่อ)
G-Man พูดว่า 'Reinstate Monica'

(ต่อ) … (2b) เชลล์ไม่ส่งสัญญาณ Ctrl + C เชลล์ไม่มีบทบาทในการส่งสัญญาณ (ยกเว้นเมื่อผู้ใช้ใช้killคำสั่งซึ่งเป็นเชลล์ในตัว) (2c) แม้ว่าเซมิโคลอนหลังจากปิด}ฟังก์ชั่นจะไม่พูดอย่างผิดพลาด แต่ก็ไม่จำเป็นและไม่แหกคอกอย่างมาก (3) แม้ว่าทุกอย่างจะถูกต้อง แต่ก็ไม่ใช่คำตอบที่ดีสำหรับคำถาม (3a) คำถามในขณะที่ค่อนข้างไม่ชัดเจนดูเหมือนว่าจะมุ่งเน้นไปที่วิธีที่นักแสดง (ผู้ใช้และกระบวนการ) เริ่มต้นสัญญาณ (เช่นส่ง ) … (ต่อ)
G-Man พูดว่า 'Reinstate Monica'

(ต่อ) ... คำตอบดูเหมือนว่าจะมุ่งเน้นไปที่สัญญาณที่สร้างขึ้นโดยเคอร์เนล (โดยเฉพาะสัญญาณที่สร้างโดยแป้นพิมพ์) และวิธีการที่ผู้รับตอบสนองต่อสัญญาณ (3b) คำถามดูเหมือนจะอยู่ที่ระดับ«ใครบางคนฆ่ากระบวนการของฉัน - ใครทำมันและอย่างไร»คำตอบกล่าวถึง API การจัดการสัญญาณ, รูทีนเคอร์เนล, การดีบักเคอร์เนล (Jprobe?), ร่องรอยเคอร์เนลสแต็คและ โครงสร้างข้อมูลเคอร์เนล IMO นั้นอยู่ในระดับที่ไม่เหมาะสมโดยเฉพาะอย่างยิ่งเนื่องจากไม่มีการอ้างอิงใด ๆ ที่ผู้อ่านสามารถเรียนรู้เพิ่มเติมเกี่ยวกับงานภายในเหล่านี้ได้มากขึ้น
G-Man กล่าวว่า 'Reinstate Monica'

1
มันเป็นบล็อกของฉันเอง .. มีร่องรอยของตัวเอง .. นั่นคือสิ่งที่ฉันต้องการ .. ทุกคนจะรู้ว่าการไหลของข้อมูลอย่างละเอียด .. การพูดคุยในอากาศไม่มีความหมาย .. แม้ว่าหลังจากที่มันละเมิดแนวทางชุมชนนี้โปรดเอาคำตอบของฉันออก channel .. นี่คือคำตอบภายในเคอร์เนลไม่ใช่ไวยากรณ์ภายใน
K_K
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.