Golang signal processing and how to implement a process exit gracefully detail

  • 2020-06-12 09:30:08
  • OfStack

Signal types in THE Linux system

Signal definitions may vary from operating system to operating system. The signals defined in POSIX are listed below.

In linux, 34-64 signals are used for real-time systems.

Command man 7 signal provides the official signal presentation. You can also use ES15en-ES16en for quick viewing

In the list, signals numbered 1 to 31 are supported by traditional UNIX and are unreliable (non-real-time), while signals numbered 32 to 63 are later expanded and called reliable (real-time). The difference between an unreliable signal and a reliable signal is that the former does not support queuing and may cause signal loss, while the latter does not.

For example, x86, ia64,ppc and s390 have three values. The first value is slpha and sparc. The middle value is ix86,ia64, ppc, s390, arm and sh. Hyphen - indicates that the architecture is not supported by this signal,

The first column is the signal name;

The second column is the corresponding signal value. It should be noted that some signal names correspond to 3 signal values, because these signal values are related to the platform. The description of 3 signal values in man manual is as follows. the first usually valid sparc, the middle for i386, ppc and sh, one one for mips.

The third list is the actions of the operating system after receiving the signal, Term indicates the default action is to terminate the process, Ign indicates the default action is to ignore the signal, Core indicates the default action is to terminate the process and output core dump, Stop indicates the default action is to stop the process.

The fourth column is an explanatory note on the role of signals.

Standard signals - ES83en.1-1990 definitions


 Signal Value Action Comment
 ----------------------------------------------------------------------
 SIGHUP 1 Term Hangup detected on controlling terminal
     or death of controlling process
 SIGINT 2 Term Interrupt from keyboard
 SIGQUIT 3 Core Quit from keyboard
 SIGILL 4 Core Illegal Instruction
 SIGABRT 6 Core Abort signal from abort(3)
 SIGFPE 8 Core Floating point exception
 SIGKILL 9 Term Kill signal
 SIGSEGV 11 Core Invalid memory reference
 SIGPIPE 13 Term Broken pipe: write to pipe with no
     readers
 SIGALRM 14 Term Timer signal from alarm(2)
 SIGTERM 15 Term Termination signal
 SIGUSR1 30,10,16 Term User-defined signal 1
 SIGUSR2 31,12,17 Term User-defined signal 2
 SIGCHLD 20,17,18 Ign Child stopped or terminated
 SIGCONT 19,18,25 Cont Continue if stopped
 SIGSTOP 17,19,23 Stop Stop process
 SIGTSTP 18,20,24 Stop Stop typed at tty
 SIGTTIN 21,21,26 Stop tty input for background process
 SIGTTOU 22,22,27 Stop tty output for background process

SIGKILL and SIGSTOP signals cannot be captured, blocked, or ignored.

Standard Signals - DEFINITION of SUSv2 and POSIX, 1-2001


 Signal Value Action Comment
 --------------------------------------------------------------------
 SIGBUS 10,7,10 Core Bus error (bad memory access)
 SIGPOLL   Term Pollable event (Sys V).
     Synonym for SIGIO
 SIGPROF 27,27,29 Term Profiling timer expired
 SIGSYS 12,-,12 Core Bad argument to routine (SVr4)
 SIGTRAP 5 Core Trace/breakpoint trap
 SIGURG 16,23,21 Ign Urgent condition on socket (4.2BSD)
 SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2BSD)
 SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2BSD)
 SIGXFSZ 25,25,31 Core File size limit exceeded (4.2BSD)

As early as Linux 2.2SIGSYS, SIGXCPU, SIGXFSZ and SIGBUS (non-sparc and mips architectures) the default operation was to terminate the process (but not to produce coredump)

In some unix systems, THE SIGXCPU and SIGXFSZ signals are used to terminate the process and do not generate coredunp. Since Linux 2.4 these signals have generated coredump.

Standard signal - other signals


  Signal  Value  Action Comment
  --------------------------------------------------------------------
  SIGIOT   6  Core IOT trap. A synonym for SIGABRT
  SIGEMT  7,-,7  Term
  SIGSTKFLT -,16,-  Term Stack fault on coprocessor (unused)
  SIGIO  23,29,22 Term I/O now possible (4.2BSD)
  SIGCLD  -,-,18  Ign  A synonym for SIGCHLD
  SIGPWR  29,30,19 Term Power failure (System V)
  SIGINFO  29,-,-    A synonym for SIGPWR
  SIGLOST  -,-,-  Term File lock lost
  SIGWINCH 28,28,20 Ign  Window resize signal (4.3BSD, Sun)
  SIGUNUSED -,31,-  Term Unused signal (will be SIGSYS)

Signal 29 is SIGINFO or SIGPWR in alpha, but SIGLOST in sparc.

SIGEMT is not defined in ES128en.1-2001, but it is not in most Unix plays. The default is coredump and terminates the process.
SIGPWR (not defined in ES133en.1-2001) is ignored by default.

SIGIO (not defined in ES138en.1-2001) is also ignored in some Unix systems.

kill pid is used to send SIGTERM (the default signal sent by kill) to a process with process number pid, which is a signal to terminate the process and can be captured by the application. If the application does not capture and respond to the logical code for the signal, the default action for the signal is kill to drop the process. This is the recommended practice for terminating a specified process.

kill-9 pid sends SIGKILL (the signal number is 9) to a process with process number pid. As explained above, SIGKILL cannot be captured, blocked or ignored by the application, and its action is to terminate the specified process immediately. In layman's terms, the application can't "sense" the SIGKILL signal at all, and it is completely unprepared and killed by the operating system receiving the SIGKILL signal. Obviously, in this "violent" situation, the application has no opportunity to free up the resources it is currently occupying. In fact, the SIGKILL signal is sent directly to the init process, which, upon receiving the signal, is responsible for terminating the process specified by pid. In some cases (such as when the process is dead with hang and cannot respond to a normal signal), es161EN-9 can be used to terminate the process.

1 if kill through the end of the process is to create a child of the parent process, will become an orphan, his son process (Orphan Process), in this case, the child process exit status can't be applied process to capture (because as a parent application no longer exists), but should not be any adverse impact on the entire linux system.

Signal transmission and processing in Go

Sometimes we want to process the Signal signal in an Go program, such as gracefully closing the program after receiving the SIGTERM signal (see the application in the next section). The Go signal notification mechanism can be sent to 1 channel os.Signal The implementation. So let's first create 1 os.Signal channel , and then use signal.Notify Register the signal to be received.


package main
import (
 "fmt"
 "os"
 "os/signal"
 "syscall"
)
func main() {
 sigs := make(chan os.Signal, 1)
 done := make(chan bool, 1)
 // signal.Notify(c)
 signal.Notify(sigs, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGINT, syscall.SIGTERM)
 go func() {
 sig := <-sigs
 fmt.Println(sig)
 done <- true
 }()

 fmt.Println("wait for signal")
 <- done
 fmt.Println("got signal and exit")
 fmt.Println("run done")
}

How do I implement a graceful exit of a process

What is a graceful exit in the first place? The so-called elegant exit is actually to avoid killing the process by violence. After receiving the signal, the process will automatically do some post-treatment and then exit voluntarily.

Linux Server server-side applications often run long time, in the process of running, may apply for a lot of system resources, can also save a lot of state, in these scenarios, we hope to process before exiting, can release the resources or the current state of dump to disk or print some important log, is hope process graceful exit (exit gracefully).

As you can see from the above introduction, graceful exits can be achieved by capturing SIGTERM. To be specific, it usually takes only two steps:

1) Register the handler function of SIGTERM signal and do some preparation for process exit in the handler function. Signal-processing function registration is available signal() or sigaction() The latter is recommended for setting up the signal response function. The simpler the logic of the signal processing function, the better. The common practice is to set an flag variable of type bool in this function to indicate that the process has received an SIGTERM signal and is ready to exit.

2) in the main process main() In, by something like while(!bQuit) 1 once bQuit is set as true in signal handler function, the main process exits the while() loop, followed by 1 action to release the resource or the current status or logging of the dump process, after which the main process exits.

This in front of me 1 article also introduced [= [golang httpserver graceful restart] [1]] https: / / www ofstack. com article / 137069. htm, it introduces the 1, how do we use httpserver graceful restart, it also introduced some signals using 1, grace and restart the train of thought. Today we are going to show you how to exit gracefully, which is a simplified version of how to restart gracefully.


package main
import (
 "fmt"
 "os"
 "os/signal"
 "syscall"
 "time"
)
 
func main() {
 sigs := make(chan os.Signal, 1)
 // done := make(chan bool, 1) 
 // signal.Notify(sigs)
 // signal.Notify(sigs, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGINT, syscall.SIGTERM)
 signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) 
 // go func() {
 // sig := <-sigs
 // fmt.Println(sig)
 // done <- true
 // }()
 go func() {
 for s := range sigs {
 switch s {
 case syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT:
 fmt.Println("got signal and try to exit: ", s)
 do_exit()
 case syscall.SIGUSR1:
 fmt.Println("usr1: ", s)
 case syscall.SIGUSR2:
 fmt.Println("usr2: ", s)
 default:
 fmt.Println("other: ", s)
 }
 }
 }()
 
 
 fmt.Println("wait for signal")
 i := 0
 for {
 i++
 fmt.Println("times: ", i)
 time.Sleep(1 * time.Second)
 }
 // <- done
 fmt.Println("got signal and exit")
 fmt.Println("run done")
}
 
func do_exit() {
 fmt.Println("try do some clear jobs")
 fmt.Println("run done")
 os.Exit(0)
}

kill -USR1 pid 
usr1 user defined signal 1
 
kill -USR2 pid 
usr2 user defined signal 2
 
kill -QUIT pid 
got signal and try to exit: quit
try do some clear jobs
run done

conclusion


Related articles: