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_signal
chứ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_struct
theo 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_SIGPENDING
cờ đượ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_INTERRUPTIBLE
hoặc TASK_UNINTERRUPTIBLE
.
Một tác vụ trong trạng thái TASK_INTERRUPTIBLE
sẽ được thay đổi thành TASK_RUNNING
trạng thái bằng tín hiệu. TASK_RUNNING
có 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 syscall
không hoàn thành theo mặc định.
Nếu SA_RESTART
cờ đượ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