Làm thế nào tín hiệu làm việc trong nội bộ?


31

Nói chung, để tiêu diệt các quá trình chúng ta tạo ra các tín hiệu như SIGKILL, SIGTSTPv.v.

Nhưng làm thế nào để biết ai đã ra lệnh cho tín hiệu cụ thể đó, ai đã gửi nó đến một quy trình cụ thể và nói chung làm thế nào để các tín hiệu thực hiện hoạt động của họ? Làm thế nào để tín hiệu làm việc trong nội bộ?


Câu hỏi hơi khó hiểu. Tôi xin lỗi và có nghĩa là không tôn trọng. Bạn có muốn biết ai có thể đã chạy một lệnh đã giết một quá trình hoặc bạn muốn biết thêm về SIGKILL và SIGSTP?
pullsumo

@mistermister Tôi muốn biết ai có thể đã chạy một lệnh giết một tiến trình và làm thế nào?
Varun Chhangani

Câu trả lời:


35

Tầm nhìn 50.000 feet là:

  1. Một tín hiệu được tạo bởi hạt nhân bên trong (ví dụ: SIGSEGVkhi địa chỉ không hợp lệ được truy cập hoặc SIGQUITkhi bạn nhấn Ctrl+ \) hoặc bởi một chương trình sử dụng killsyscall (hoặc một vài địa chỉ liên quan).

  2. Nếu đó là bởi một trong các tòa nhà, thì hạt nhân xác nhận quá trình gọi có đủ đặc quyền để gửi tín hiệu. Nếu không, lỗi sẽ được trả về (và tín hiệu không xảy ra).

  3. Nếu đó là một trong hai tín hiệu đặc biệt, hạt nhân hoạt động vô điều kiện trên nó, mà không có bất kỳ đầu vào nào từ quá trình đích. Hai tín hiệu đặc biệt là SIGKILL và SIGSTOP. Tất cả những thứ bên dưới về các hành động mặc định, tín hiệu chặn, v.v., đều không liên quan đến hai thứ này.

  4. Tiếp theo, kernel tìm ra những gì làm với tín hiệu:

    1. Đối với mỗi quá trình, có một hành động liên quan đến từng tín hiệu. Có một loạt các mặc định và các chương trình có thể thiết lập các chương trình khác nhau bằng cách sử dụng sigaction, signalv.v. Chúng bao gồm những thứ như "bỏ qua hoàn toàn", "giết quá trình", "giết tiến trình với kết xuất lõi", "dừng quá trình", v.v.

    2. Các chương trình cũng có thể tắt việc phân phối tín hiệu ("bị chặn"), trên cơ sở tín hiệu theo tín hiệu. Sau đó tín hiệu vẫn chờ xử lý cho đến khi bỏ chặn.

    3. Các chương trình có thể yêu cầu thay vì nhân thực hiện một số hành động, nó sẽ truyền tín hiệu đến tiến trình một cách đồng bộ (với sigwait, và các cộng sự hoặc signalfd) hoặc không đồng bộ (bằng cách làm gián đoạn bất cứ quy trình nào đang thực hiện và gọi một hàm được chỉ định).

Có một bộ tín hiệu thứ hai gọi là "tín hiệu thời gian thực", không có ý nghĩa cụ thể và cũng cho phép nhiều tín hiệu được xếp hàng (tín hiệu bình thường chỉ xếp hàng một tín hiệu khi tín hiệu bị chặn). Chúng được sử dụng trong các chương trình đa luồng để các luồng giao tiếp với nhau. Một số được sử dụng trong triển khai chủ đề POSIX của glibc, ví dụ. Chúng cũng có thể được sử dụng để liên lạc giữa các quy trình khác nhau (ví dụ: bạn có thể sử dụng một số tín hiệu thời gian thực để có chương trình fooctl gửi tin nhắn đến foo daemon).

Đối với chế độ xem không phải 50.000 feet, hãy thử man 7 signalvà cả tài liệu bên trong kernel (hoặc nguồn).


"Hai tín hiệu đặc biệt là SIGKILL và SIGSTOP", vậy SIGCONT có thể là gì ...
Hauke ​​Laging

@HaukeLaging SIGCONT là tín hiệu hoàn tác SIGSTOP. Tài liệu này không liệt kê nó là đặc biệt ... Vì vậy, tôi không chắc chắn về mặt kỹ thuật một quy trình có thể đặt nó thành bỏ qua hay không, thì bạn sẽ không thể tiếp tục nó (chỉ SIGKILL nó).
derobert

22

Tín hiệu thực hiện là rất phức tạp và là hạt nhân cụ thể. Nói cách khác, các hạt nhân khác nhau sẽ thực hiện các tín hiệu khác nhau. Một lời giải thích đơn giản như sau:

CPU, dựa trên một giá trị thanh ghi đặc biệt, có một địa chỉ trong bộ nhớ nơi nó dự kiến ​​sẽ tìm thấy một "bảng mô tả ngắt" thực sự là một bảng vectơ. Có một vectơ cho mọi ngoại lệ có thể, như chia cho 0 hoặc bẫy, như INT 3 (gỡ lỗi). Khi CPU gặp ngoại lệ, nó sẽ lưu các cờ và con trỏ lệnh hiện tại trên ngăn xếp và sau đó nhảy đến địa chỉ được chỉ định bởi vectơ có liên quan. Trong Linux, vectơ này luôn trỏ vào kernel, nơi có một trình xử lý ngoại lệ. CPU đã hoàn thành và nhân Linux tiếp quản.

Lưu ý rằng bạn cũng có thể kích hoạt một ngoại lệ từ phần mềm. Ví dụ, người dùng nhấn CTRL- C, sau đó cuộc gọi này đi đến kernel gọi trình xử lý ngoại lệ của chính nó. Nói chung, có nhiều cách khác nhau để xử lý trình xử lý, nhưng bất kể điều cơ bản tương tự xảy ra: bối cảnh được lưu trên ngăn xếp và trình xử lý ngoại lệ của kernel được chuyển đến.

Trình xử lý ngoại lệ sau đó quyết định luồng nào sẽ nhận tín hiệu. Nếu một cái gì đó giống như chia cho 0 xảy ra, thì thật dễ dàng: luồng gây ra ngoại lệ nhận được tín hiệu, nhưng đối với các loại tín hiệu khác, quyết định có thể rất phức tạp và trong một số trường hợp bất thường, một sợi ngẫu nhiên ít nhiều có thể nhận được tín hiệu.

Để gửi tín hiệu, hạt nhân làm gì trước tiên là đặt một giá trị cho biết loại tín hiệu SIGHUPhoặc bất cứ thứ gì. Đây chỉ là một số nguyên. Mọi quá trình đều có vùng nhớ "tín hiệu chờ xử lý" nơi lưu trữ giá trị này. Sau đó, kernel tạo ra một cấu trúc dữ liệu với thông tin tín hiệu. Cấu trúc này bao gồm một "bố trí" tín hiệu có thể được mặc định, bỏ qua hoặc xử lý. Nhân sau đó gọi chức năng của chính nó do_signal(). Giai đoạn tiếp theo bắt đầu.

do_signal()đầu tiên quyết định xem sẽ xử lý tín hiệu. Ví dụ, nếu đó là một vụ giết , thì do_signal()chỉ cần giết chết quá trình, kết thúc câu chuyện. Mặt khác, nó nhìn vào bố trí. Nếu bố trí là mặc định, sau đó do_signal()xử lý tín hiệu theo chính sách mặc định phụ thuộc vào tín hiệu. Nếu xử lý được xử lý, điều đó có nghĩa là có một chức năng trong chương trình người dùng được thiết kế để xử lý tín hiệu được đề cập và con trỏ tới chức năng này sẽ nằm trong cấu trúc dữ liệu nói trên. Trong trường hợp này do_signal () gọi một hàm kernel khác,handle_signal(), sau đó trải qua quá trình chuyển trở lại chế độ người dùng và gọi chức năng này. Các chi tiết của bàn giao này là vô cùng phức tạp. Mã này trong chương trình của bạn thường được liên kết tự động vào chương trình của bạn khi bạn sử dụng các chức năng trong signal.h.

Bằng cách kiểm tra giá trị tín hiệu đang chờ một cách thích hợp, hạt nhân có thể xác định xem quy trình có xử lý tất cả các tín hiệu hay không và sẽ có hành động thích hợp nếu không, điều này có thể khiến quá trình ngủ hoặc giết nó hoặc hành động khác, tùy thuộc vào tín hiệu.


15

Mặc dù câu hỏi này đã được trả lời, hãy để tôi đăng một luồng chi tiết các sự kiện trong nhân Linux.
Điều này được sao chép hoàn toàn từ các bài đăng của Linux: Tín hiệu Linux - Nội bộ tại các bài đăng trên Linux Linux trên blog của sklinuxblog.blogspot.in.

Tín hiệu chương trình không gian người dùng C

Hãy bắt đầu bằng cách viết một chương trình C không gian người dùng tín hiệu đơn giản:

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

Mã này gán một trình xử lý mới cho tín hiệu SIGINT. SIGINT có thể được gửi đến quá trình đang chạy bằng tổ hợp phím Ctrl+ C. Khi nhấn Ctrl+ Cthì tín hiệu không đồng bộ SIGINT được gửi đến tác vụ. Nó cũng tương đương với việc gửi kill -INT <pid>lệnh trong thiết bị đầu cuối khác.

Nếu bạn thực hiện kill -l(đó là một chữ thường L, viết tắt của danh sách trên máy tính), bạn sẽ biết các tín hiệu khác nhau có thể được gửi đến một quy trình đang chạy.

[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

Ngoài ra tổ hợp phím sau có thể được sử dụng để gửi tín hiệu cụ thể:

  • Ctrl+ C- gửi SIGINT hành động mặc định là chấm dứt ứng dụng.
  • Ctrl+ \  - gửi SIGQUIT hành động mặc định là chấm dứt lõi bán phá giá ứng dụng.
  • Ctrl+ Z- gửi SIGSTOP tạm dừng chương trình.

Nếu bạn biên dịch và chạy chương trình C ở trên thì bạn sẽ nhận được đầu ra sau:

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

Ngay cả với Ctrl+ Choặc kill -2 <pid>quá trình sẽ không chấm dứt. Thay vào đó, nó sẽ thực hiện xử lý tín hiệu và trở về.

Làm thế nào tín hiệu được gửi đến quá trình

Nếu chúng ta thấy phần bên trong của tín hiệu gửi đến một tiến trình và đặt Jprobe với dump_stack vào __send_signalchức năng, chúng ta sẽ thấy dấu vết cuộc gọi sau:

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

Vì vậy, chức năng chính gọi để gửi tín hiệu như sau:

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.

Bây giờ mọi thứ đã được thiết lập và các thay đổi cần thiết được thực hiện task_structtheo quy trình.

Xử lý tín hiệu

Tín hiệu được kiểm tra / xử lý bởi một quá trình khi nó trở về từ cuộc gọi hệ thống hoặc nếu việc hoàn trả từ ngắt được thực hiện. Sự trở lại từ cuộc gọi hệ thống có trong tập tin entry_64.S.

Hàm int_signal được gọi từ entry_64.Sđó gọi hàm do_notify_resume().

Hãy kiểm tra chức năng do_notify_resume(). Hàm này kiểm tra nếu chúng ta có TIF_SIGPENDINGcờ được đặt trong 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;

Các cuộc gọi và tín hiệu HỆ THỐNG

Các tòa nhà cao tầng của Slow Slow, ví dụ như chặn đọc / ghi, đưa các quá trình vào trạng thái chờ: TASK_INTERRUPTIBLEhoặc TASK_UNINTERRUPTIBLE.

Một tác vụ trong trạng thái TASK_INTERRUPTIBLEsẽ được thay đổi thành TASK_RUNNINGtrạng thái bằng tín hiệu. TASK_RUNNINGcó nghĩa là một quá trình có thể được lên lịch.

Nếu được thực thi, trình xử lý tín hiệu của nó sẽ được chạy trước khi hoàn thành tòa nhà chậm chậm. Các syscallkhông hoàn thành theo mặc định.

Nếu SA_RESTARTcờ được đặt, syscallđược khởi động lại sau khi xử lý tín hiệu kết thúc.

Tài liệu tham khảo


Cảm ơn bạn đã nỗ lực đóng góp cho trang web, nhưng (1) nếu bạn sẽ sao chép tài liệu từ một trang web khác (từng chữ, từng chữ, bao gồm cả lỗi ngữ pháp và dấu câu), bạn nên nói rằng bạn đang làm vì vậy, rõ ràng hơn nhiều Liệt kê nguồn là một Tài liệu tham khảo trực tuyến, trong khi cần thiết, là không đủ. Trừ khi bạn là tác giả của blog (K_K = sk?), Trong trường hợp đó bạn không bắt buộc phải liên kết với nó - nhưng, nếu bạn làm thế, bạn phải tiết lộ (tức là nói) rằng đó là của bạn. Tiết (Cont'd)
G-Man nói 'Phục hồi Monica'

(Tiếp theo) Nhiều (2) Nguồn của bạn (blog bạn đã sao chép từ) không tốt lắm. Đã bốn năm kể từ khi câu hỏi được hỏi; bạn không thể tìm thấy một tài liệu tham khảo tốt hơn để sao chép từ? (Nếu bạn là tác giả gốc, xin lỗi.) Ngoài các lỗi ngữ pháp và dấu câu đã nói ở trên (và nói chung là từ ngữ cẩu thả và định dạng kém), điều đó là sai. (2a) Ctrl + Z gửi SIGTSTP, không phải SIGSTOP. (SIGTSTP, như SIGTERM, có thể bị bắt; SIGSTOP, như SIGKILL, không thể.) Hồi (Tiếp)
G-Man nói 'Phục hồi Monica'

(Tiếp theo), (2b) Shell không gửi tín hiệu Ctrl + C. Shell không có vai trò trong việc gửi tín hiệu (trừ khi người dùng sử dụng killlệnh, đó là shell dựng sẵn). (2c) Mặc dù dấu chấm phẩy sau khi đóng }hàm không phải là lỗi nghiêm trọng, nhưng chúng không cần thiết và rất không chính thống. (3) Ngay cả khi mọi thứ đều đúng, nó sẽ không phải là một câu trả lời hay cho câu hỏi. (3a) Câu hỏi, trong khi có phần không rõ ràng, dường như đang tập trung vào cách các tác nhân (người dùng và quá trình) khởi tạo tín hiệu (tức là gửi ). Tiết (Cont'd)
G-Man nói 'Phục hồi Monica'

(Tiếp theo) Câu trả lời dường như tập trung vào các tín hiệu do hạt nhân tạo ra (cụ thể là tín hiệu do bàn phím tạo ra) và cách quá trình người nhận phản ứng với tín hiệu. (3b) Câu hỏi dường như ở cấp độ «Ai đó đã giết quá trình của tôi - ai đã làm điều đó và làm thế nào?» Câu trả lời thảo luận về API xử lý tín hiệu, thói quen nhân, gỡ lỗi kernel (Jprobe?), Dấu vết ngăn xếp kernel và cấu trúc dữ liệu kernel. IMO, mức độ thấp không phù hợp - đặc biệt là vì nó không cung cấp bất kỳ tài liệu tham khảo nào mà người đọc có thể tìm hiểu thêm về các hoạt động nội bộ này.
G-Man nói 'Phục hồi Monica'

1
Đó là blog của riêng tôi .. dấu vết của riêng tôi .. đó là những gì tôi muốn .. mọi người sẽ biết dòng chảy chi tiết như vậy .. nói chuyện trong không khí là vô nghĩa .. ngay cả khi sau khi nó vi phạm nguyên tắc cộng đồng này, vui lòng xóa câu trả lời của tôi thông qua kênh .. đây là câu trả lời nội bộ kernel không phải ngữ pháp bên trong.
K_K
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.