Linux信號與golang中的捕獲處理


什么是信號

在計算機科學中,信號是Unix、類Unix以及其他POSIX兼容的操作系統中進程間通訊的一種有限制的方式。它是一種異步的通知機制,用來提醒進程一個事件已經發生。

當一個信號發送給一個進程,操作系統中斷了進程正常的控制流程,如果進程定義了對信號的處理,此時,程序將進入捕獲到的信號對應的處理函數,否則執行默認的處理函數。

Linux中信號的介紹

在Linux系統共定義了64種信號,分為兩大類:實時信號非實時信號,1-31為非實時,32-64種為實時信號。

  • 非實時信號: 也稱為不可靠信號,為早期Linux所支持的信號,不支持排隊,信號可能會丟失, 比如發送多次相同的信號, 進程只能收到一次. 信號值取值區間為1~31;
  • 實時信號: 也稱為可靠信號,支持排隊, 信號不會丟失, 發多少次, 就可以收到多少次. 信號值取值區間為32~64

Linux操作系統中,在終端上執行 kill -l 便可看到系統定義的所有信號

信號表

POSIX.1-1990標准信號

此表參考自:POSIX信號

信號 動作 說明
SIGHUP 1 Term 終端控制進程結束(終端連接斷開)
SIGINT 2 Term 用戶發送INTR字符(Ctrl+C)觸發
SIGQUIT 3 Core 用戶發送QUIT字符(Ctrl+/)觸發
SIGILL 4 Core 非法指令(程序錯誤、試圖執行數據段、棧溢出等)
SIGABRT 6 Core 調用abort函數觸發
SIGFPE 8 Core 算術運行錯誤(浮點運算錯誤、除數為零等)
SIGKILL 9 Term 無條件結束程序(不能被捕獲、阻塞或忽略)
SIGSEGV 11 Core 無效內存引用(試圖訪問不屬於自己的內存空間、對只讀內存空間進行寫操作)
SIGPIPE 13 Term 消息管道損壞(FIFO/Socket通信時,管道未打開而進行寫操作)
SIGALRM 14 Term 時鍾定時信號
SIGTERM 15 Term 結束程序(可以被捕獲、阻塞或忽略)
SIGUSR1 30,10,16 Term 用戶保留
SIGUSR2 31,12,17 Term 用戶保留
SIGCHLD 20,17,18 Ign 子進程結束(由父進程接收)
SIGCONT 19,18,25 Cont 繼續執行已經停止的進程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止進程(不能被捕獲、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止進程(可以被捕獲、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后台程序從終端中讀取數據時觸發
SIGTTOU 22,22,27 Stop 后台程序向終端中寫數據時觸發

更多的信號說明請查閱man7

此表的操作為每個信號的默認配置,如下所示

動作 說明
Term 默認操作是,終止進程。
Ign 默認操作是,忽略信號。
Core 默認操作是,終止該進程並核心轉儲
Stop 默認操作是,停止進程。
Cont 默認操作是,如果當前已停止,則繼續該進程。

信號的產生

​信號是事件發生時對進程的通知機制。信號中斷與硬件中斷的相似之處在於打斷了程序執行的正常流程。

信號事件的來源分為軟件信號和硬件信號:

  • 硬件信號: 用戶輸入:比如在終端上按下組合鍵ctrl+C,產生SIGINT信號;硬件異常:CPU檢測到內存非法訪問等異常,通知內核生成相應信號,並發送給發生事件的進程;
  • 軟件信號: 通過系統調用: 如,發送signal信號:killraise等。

發送的信號

  • Ctrl-C 發送 INT signal (SIGINT),通常導致進程結束
  • Ctrl-Z 發送 TSTP signal (SIGTSTP); 通常導致進程掛起(suspend)
  • Ctrl-\ 發送 QUIT signal (SIGQUIT); 通常導致進程結束 和 dump core.

信號的處理

內核處理進程收到的signal是在當前進程的上下文,故進程必須是Running狀態。當進程喚醒或者調度后獲取CPU,則會從內核態轉到用戶態時檢測是否有signal等待處理,處理完,進程會把相應的未決信號從鏈表中去掉。

signal信號處理時機: 內核 ==> 信號處理 ==> 用戶

  1. 內核態:在內核態,signal信號不起作用;
  2. signal信號處理: 在用戶態,signal所有未被屏蔽的信號都處理完畢;當屏蔽信號,取消屏蔽時,會在下一次內核轉用戶態的過程中執行;

信號處理方式

進程對信號的處理方式有3種:

  • 默認 接收到信號后按默認的行為處理該信號。 這種方式為多數應用采取的處理方式。
  • 自定義處理 用自定義的信號處理函數來執行特定的動作
  • 忽略忽略信號 接收到信號后不做任何反應。

對信號的處理動作:

  • Term: 中止進程
  • Ign: 忽略信號
  • Core: 中止進程並保存內存信息
  • Stop: 停止進程
  • Cont: 繼續運行進程

Linux信號命令

kill

kill命令用來終止指定的進程, 對於一個后台進程就須用kill命令來終止,我們就需要先使用ps/pidof/pstree/top等工具獲取進程PID,然后使用kill命令來殺掉該進程。

命令格式
kill[參數] [進程id]

命令參數
-l 信號,若果不加信號的編號參數,則使用“-l”參數會列出全部的信號名稱
-a 當處理當前進程時,不限制命令名和進程號的對應關系
-p 指定kill 命令只打印相關進程的進程號,而不發送任何信號
-s 指定發送信號
-u 指定用戶

killall

Linux系統中的killall用於殺死指定名字的進程(kill processes by name)。我們可以使用kill命令殺死指定進程PID的進程,如果要找到我們需要殺死的進程,我們還需要在之前使用ps等命令再配合grep來查找進程,而killall把這兩個過程合二為一,是一個很好用的命令。

命令格式
killall[參數] [進程名]

命令參數
-I 忽略小寫
-a 當處理當前進程時,不限制命令名和進程號的對應關系
-i 交互模式,殺死進程前先詢問用戶
-s 發送指定的信號
-w 等待進程死亡
-e 要求匹配進程名稱

PKILL

pkillkillall 使用方法類似,用於殺死指定名稱的進程

Go語言中的Signal的使用

在Go語言中,處理信號僅需要3個步驟即可完成對信號的處理

  • 信號的接收: signalChan := make(chan os.Signal,1)
  • 信號的監聽捕獲: signal.Notify(signalChan)
  • 信號的觸發: signal := <-signalChan

注意事項:

  • SIGKILL kill -9SIGSTOP kill -19 信號可能不會被Notify方法捕獲,因此無法處理這些信號。
  • 如果在Notify方法中沒有指定信號作為參數,那么該方法將捕獲所有的信號。

在Go語言中的Signal的處理

在某些場景下,如,在大量並發及,批量處理未完成時,此時需要在Go程序中處理Signal信號,比如收到SIGTERM信號后優雅的關閉程序。

實例:在一個計算場景下,有5個goroutine在處理業務,當收到 kill -15時計算完成后退出程序, kill -4不做處理。

package main

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var wg sync.WaitGroup

func exitProcess() {
	fmt.Println("等待進程完成")
	wg.Wait()
	fmt.Println("進程退出")
}

func process(n int) {
	i := n
	for {
		fmt.Println("process", n, ":", i)
		if i > 100 {
			break
		}
		time.Sleep(time.Second)
		i++
	}
	fmt.Println("process", n, "finnshed")
	defer wg.Done()
}

func main() {
	signals := make(chan os.Signal, 1)
	done := make(chan bool, 1)

	signal.Notify(signals, syscall.SIGILL, syscall.SIGTERM)

	go func() {
		for signal := range signals {
			switch signal {
			case syscall.SIGTERM, syscall.SIGQUIT:
				fmt.Println("kill -15 進程退出")
				exitProcess()
			case syscall.SIGILL:
				fmt.Println("kill -4")
			}
		}
		done <- true
	}()

	wg.Add(5)
	for n := 0; n < 10; n++ {
		go process(n)
	}

	fmt.Println("waiting signal...")
	wg.Wait()
	fmt.Println("exiting")
}

收到kill -4 信號打印kill -4

收到kill -15 信號后,帶程序處理完成后退出

Go進程間通訊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM