1. Basic Knowledge Consolidation
1.1 interrupts fall into two categories:
A) hard interrupt, which is usually said interrupt, interrupt handler running in the kernel state, need some hardware support;
b) Soft interrupt, is a simulation of the interrupt at the software level, is often said signal , its processing program runs in the user state . It is software-level and does not require specific hardware support.
1.2 Common signals : (can be viewed with kill-l and man 7 signal commands)
A) Sigint:ctrl+c, 2
b) sigquit:ctrl+\, 3
c) sigkill:kill-9 PID command, 9
d) Sigpipe: related to closed TCP connection, 13
e) SIGCHLD: Child process dies, zombie process, 17
f) Sigalarm:alarm function, 14
1.3 Terminology relating to signals:
A) send a signal;
b) receive the signal.
1.4 There are three ways to handle the signal (note here that the way to kill a process is to kill-9 pid or Pkill filaname)
a) Write your own handler
b) Ignore
c) adopt the system default behavior
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define Err_exit (m) do { perror (m); Exit (exit_failure); } while (0)/* * Handle CTRL + C signal * Ignore CTRL + \ signal */void handler (int signum) { printf (" Catch Ctrl + C\n");} int main (int argc, const char *argv[]) { if (signal (SIGINT, handler) = = Sig_err) { err_exit ("signal"); } if (Signal (sigquit, sig_ign) = = Sig_err) {//ctrl + err_exit ("signal"); } for (;;) Pause (); Pause the program until you receive the signal return 0;}
2. Sending and receiving signals
2.1 How to send a signal:
A)kill-signum pid command : Send a signum signal to the PID process, for example, Kill-9 pid is to send Sigkill signal to a process to kill the process, Kill-2 PID is to send a CTRL + C signal to a process, so that the process end Check
b) Use the system to invoke the Kill function;
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define Err_exit (m) do { perror (m); Exit (exit_failure); } while (0)/* The Kill function sends a signal to a process */void handler (int signum) { printf (" catch Ctrl + c\n");} int main (int argc, const char *argv[]) { if (signal (SIGINT, handler) = = Sig_err) { err_exit ("signal"); } Sleep (3);//Sleep three seconds after sending SIGINT signal to this process if (Kill (Getpid (), SIGINT) = = 1) { err_exit ("Kill"); } for (;;) Pause (); Pause the program until you receive the signal return 0;}
c)alarm function, send yourself a SIGALRM signal , note that each process can only have a alarm timer, the new will replace the old one.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define Err_exit (m) do { perror (m); Exit (exit_failure); } while (0)/* * Alarm function sends itself the Sigalam function */void handler (int signum) { printf (" catch sigalrm\n");} int main (int argc, const char *argv[]) { if (signal (SIGALRM, handler) = = Sig_err) { err_exit ("signal"); } Alarm (3); for (;;) Pause (); Pause the program until you receive the signal return 0;}
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define Err_exit (m) do { perror (m); Exit (exit_failure); } while (0)/* * Alarm timer */void handler (int signum) { static int beeps = 0; printf ("beep\n"); if (++beeps < 5) { alarm (1); } else{ printf ("boom\n"); Exit (exit_success);} } int main (int argc, const char *argv[]) { if (signal (SIGALRM, handler) = = Sig_err) { err_exit ("signal"); } Alarm (3); for (;;) Pause (); return 0;}
2.2 The sleep function can be interrupted by a signal .
2.2.1 such as this example, the program first sleeps 10s, if in this 10s pressed CTRL + C is sent to the program to send the signal, signal processing is complete, at this time the program will not re-execute sleep, but from the next line of sleep to start execution.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define Err_exit (m) do { perror (m); Exit (exit_failure); } while (0)/* The Sleep function can be interrupted by a signal */void handler (int signum) { printf (" catch Ctrl + c\n");} int main (int argc, const char *argv[]) { if (signal (SIGINT, handler) = = Sig_err) { err_exit ("signal"); } Sleep (10);
printf ("After handler\n"); return 0;}
2.2.2 Because the Sleep function returns the number of seconds left without sleep, you can use a loop that causes the program to continue to sleep until it is interrupted.
#include <stdio.h> #include <stdlib.h> #include < string.h> #include <signal.h> #define ERR_EXIT (m) do {perror (M); Exit (Exit_failure); }while (0)/* * Sleep returns the number of seconds left without sleep * therefore you can use a loop to continue the interrupted sleep */void handler (int signum) {printf ("Catch Ctrl + c\n");} int main (int argc, const char *argv[]) {if (signal (SIGINT, handler) = = Sig_err) {err_exit ("signal"); }//sleep (10); If there is a signal directly over the sleep period int n = 10; do{n = sleep (n); printf ("Interupted left%d seconds\n", n); }while (n > 0); return 0;}
2.3 signals that have been sent but not yet received are called pending signals . The system uses a vector to represent the set of signals to be processed, so there is at most one pending signal for each type of signal. A pending signal can be received at most once.
3. Signal Processing
some problems caused by 3.1 signal processing:
A) The pending signal is blocked : If the program is processing SIGINT, then a SIGINT will be blocked to become a pending signal, until the last SIGINT handler returns.
b) Pending signals are not queued : Each type of signal finally has only one pending signal, if there is already a SIGINT pending signal in the program, when a SIGINT is reached,it will be discarded.
c) system calls can be interrupted by handler , that is, eintr, for example,read(fd ...). ) is a slow system call that, if a signal arrives , switches to the handler of the signal, and when returned from that handler,read returns-1, errno is eintr.
3.2 Examples. The parent process generates 10 sub-processes, and each child process exits automatically after performing some operations, at which time the parent process receives 10 SIGCHLD signals ( These 10 signals are emitted almost at the same moment ). The parent process then begins to process the first SIGCHLD handler, which sleep 2s, during which the second signal arrives, is blocked as a pending signal, and the signal behind is simply discarded . As a result, the sub-processes behind the program become zombie processes.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #define N 10# Define ERR_EXIT (m) do { perror (m); Exit (exit_failure); } while (0)/* * pending signal can only have one * other will be discarded *///processing sigchld signal, to prevent the production of zombie process void handler (int signum) { pid_t pid; if (PID = Waitpid ( -1, NULL, 0)) < 0) { err_exit ("Waitpid"); } printf ("Handler process child%d\n", PID); Sleep (2); Intentionally blocking other SIGCHLD signals, only one SIGCHLD signal processing is completed to process the next}int main (int argc, const char *argv[]) { if (signal (SIGCHLD, handler) = = Sig_err) { err_exit ("signal"); } pid_t pid; int i; for (i = 0; i < N; i++) { if (PID = fork ()) < 0) { err_exit ("fork"); } else if (PID = = 0) { printf ("Child pid:%d\n", Getpid ()); Sleep (2); Exit (exit_success); } } for (;;) Pause (); Pause the program until you receive the signal return 0;}
3.3 Key ideas to solve the above problems:
a) the vector corresponding to the signal is set to 1, indicating that there is at least one such signal;
b) When processing SIGCHLD signals, you should handle as many sub-processes as possible.
3.4 Examples. Improvements to the example above. Program Analysis:
A) 10 sub-processes are almost simultaneously extinct, emitting 10 SIGCHLD signals;
b) The parent process receives a SIGCHLD, executes Waitpid, reclaims the first child process, and then sleeps 2 s;
c) Within this 2 s, the second SIGCHLG becomes pending and the remainder is lost;
D) The end of sleep, there are 9 zombie processes, and then execute 9+1 times while loop, all processing completed;
e) above belong to the first SIGCHLD handler;
f) Handle the second SIGCHLD, at which time there are no child processes, Waitpid fail, exit.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include < unistd.h> #define N 3#define err_exit (m) do {perror (M); Exit (Exit_failure); }while (0)/* multi-processing SIGCHLD signal */void handler (int signum) {pid_t pid; while (PID = Waitpid ( -1, NULL, 0)) > 0) {printf ("Handler process child%d\n", PID); Sleep (2); } printf ("end\n");} int main (int argc, const char *argv[]) {if (signal (SIGCHLD, handler) = = Sig_err) {err_exit ("signal"); } pid_t pid; int i; for (i = 0; i < N; i++) {if (PID = fork ()) < 0) {err_exit ("fork"); } else if (PID = = 0) {printf ("Child pid:%d\n", Getpid ()); Sleep (2); Exit (exit_success); }} int n; Char buf[1024] = {0}; Blocking here//linux automatically restarts the system call if ((n = Read (Stdin_fileno, buf, 1024x768)) < 0) {err_exit ("read"); } printf ("Parents process input\n"); Return 0;}
3.5 Handling Zombie Processes:
A) write the synchronization code in the parent process, and you can reclaim the child process individually, either sequentially or asynchronously . the defect of this method is the need to accurately grasp the extinction time of the sub-process.
b) processing the SIGCHLD signal, this method provides an asynchronous processing capability. It should be noted that when processing SIGCHLD signals, you should use the while loop to handle as many sub-processes as possible, mainly to prevent signal blocking and loss problems.