Linux訊號(signal)機制

wongchaofan發表於2024-06-10

訊號(signal)是一種軟中斷,訊號機制是程序間通訊的一種方式,採用非同步通訊方式

一、訊號型別

Linux系統共定義了64種訊號,分為兩大類:可靠訊號與不可靠訊號,前32種訊號為不可靠訊號,後32種為可靠訊號。

1.1 概念

  • 不可靠訊號: 也稱為非實時訊號,不支援排隊,訊號可能會丟失, 比如傳送多次相同的訊號, 程序只能收到一次. 訊號值取值區間為1~31;

  • 可靠訊號: 也稱為實時訊號,支援排隊, 訊號不會丟失, 發多少次, 就可以收到多少次. 訊號值取值區間為32~64

1.2 訊號表

在終端,可透過kill -l檢視所有的signal訊號

取值名稱解釋預設動作
1 SIGHUP 掛起
2 SIGINT 中斷
3 SIGQUIT 退出
4 SIGILL 非法指令
5 SIGTRAP 斷點或陷阱指令
6 SIGABRT abort發出的訊號
7 SIGBUS 非法記憶體訪問
8 SIGFPE 浮點異常
9 SIGKILL kill訊號 不能被忽略、處理和阻塞
10 SIGUSR1 使用者訊號1
11 SIGSEGV 無效記憶體訪問
12 SIGUSR2 使用者訊號2
13 SIGPIPE 管道破損,沒有讀端的管道寫資料
14 SIGALRM alarm發出的訊號
15 SIGTERM 終止訊號
16 SIGSTKFLT 棧溢位
17 SIGCHLD 子程序退出 預設忽略
18 SIGCONT 程序繼續
19 SIGSTOP 程序停止 不能被忽略、處理和阻塞
20 SIGTSTP 程序停止
21 SIGTTIN 程序停止,後臺程序從終端讀資料時
22 SIGTTOU 程序停止,後臺程序想終端寫資料時
23 SIGURG I/O有緊急資料到達當前程序 預設忽略
24 SIGXCPU 程序的CPU時間片到期
25 SIGXFSZ 檔案大小的超出上限
26 SIGVTALRM 虛擬時鐘超時
27 SIGPROF profile時鐘超時
28 SIGWINCH 視窗大小改變 預設忽略
29 SIGIO I/O相關
30 SIGPWR 關機 預設忽略
31 SIGSYS 系統呼叫異常

對於signal訊號,絕大部分的預設處理都是終止程序或停止程序,或dump核心映像轉儲。 上述的31的訊號為非實時訊號,其他的訊號32-64 都是實時訊號。

二、訊號產生

訊號來源分為硬體類和軟體類:

2.1 硬體方式

  • 使用者輸入:比如在終端上按下組合鍵ctrl+C,產生SIGINT訊號;
  • 硬體異常:CPU檢測到記憶體非法訪問等異常,通知核心生成相應訊號,併傳送給發生事件的程序;

2.2 軟體方式

透過系統呼叫,傳送signal訊號:kill(),raise(),sigqueue(),alarm(),setitimer(),abort()

  • kernel,使用 kill_proc_info()等
  • native,使用 kill() 或者raise()等
  • java,使用 Procees.sendSignal()等

三、訊號註冊和登出

3.1 註冊

在程序task_struct結構體中有一個未決訊號的成員變數 struct sigpending pending。每個訊號在程序中註冊都會把訊號值加入到程序的未決訊號集。

  • 非實時訊號傳送給程序時,如果該資訊已經在程序中註冊過,不會再次註冊,故訊號會丟失;
  • 實時訊號傳送給程序時,不管該訊號是否在程序中註冊過,都會再次註冊。故訊號不會丟失;

3.2 登出

  • 非實時訊號:不可重複註冊,最多隻有一個sigqueue結構;當該結構被釋放後,把該訊號從程序未決訊號集中刪除,則訊號登出完畢;
  • 實時訊號:可重複註冊,可能存在多個sigqueue結構;當該訊號的所有sigqueue處理完畢後,把該訊號從程序未決訊號集中刪除,則訊號登出完畢;

四、訊號處理

核心處理程序收到的signal是在當前程序的上下文,故程序必須是Running狀態。當程序喚醒或者排程後獲取CPU,則會從核心態轉到使用者態時檢測是否有signal等待處理,處理完,程序會把相應的未決訊號從連結串列中去掉。

4.1 處理時機

signal訊號處理時機: 核心態 -> signal訊號處理 -> 使用者態:

  • 在核心態,signal訊號不起作用;
  • 在使用者態,signal所有未被遮蔽的訊號都處理完畢;
  • 當遮蔽訊號,取消遮蔽時,會在下一次核心轉使用者態的過程中執行;

4.2 處理方式

程序對訊號的處理方式: 有3種

  • 預設 接收到訊號後按預設的行為處理該訊號。 這是多數應用採取的處理方式。
  • 自定義 用自定義的訊號處理函式來執行特定的動作
  • 忽略 接收到訊號後不做任何反應。

4.3 訊號安裝

程序處理某個訊號前,需要先在程序中安裝此訊號。安裝過程主要是建立訊號值和程序對相應資訊值的動作。

訊號安裝函式

  • signal():不支援訊號傳遞資訊,主要用於非實時訊號安裝;
  • sigaction():支援訊號傳遞資訊,可用於所有訊號安裝;

其中 sigaction結構體

  • sa_handler:訊號處理函式
  • sa_mask:指定訊號處理程式執行過程中需要阻塞的訊號;
  • sa_flags:標示位
    • SA_RESTART:使被訊號打斷的syscall重新發起。
    • SA_NOCLDSTOP:使父程序在它的子程序暫停或繼續執行時不會收到 SIGCHLD 訊號。
    • SA_NOCLDWAIT:使父程序在它的子程序退出時不會收到SIGCHLD訊號,這時子程序如果退出也不會成為僵 屍程序。
    • SA_NODEFER:使對訊號的遮蔽無效,即在訊號處理函式執行期間仍能發出這個訊號。
    • SA_RESETHAND:訊號處理之後重新設定為預設的處理方式。
    • SA_SIGINFO:使用sa_sigaction成員而不是sa_handler作為訊號處理函式。

函式原型:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  • signum:要操作的signal訊號。
  • act:設定對signal訊號的新處理方式。
  • oldact:原來對訊號的處理方式。
  • 返回值:0 表示成功,-1 表示有錯誤發生。

4.4 訊號傳送

  • kill():用於向程序或程序組傳送訊號;
  • sigqueue():只能向一個程序傳送訊號,不能像程序組傳送訊號;主要針對實時訊號提出,與sigaction()組合使用,當然也支援非實時訊號的傳送;
  • alarm():用於呼叫程序指定時間後發出SIGALARM訊號;
  • setitimer():設定定時器,計時達到後給程序傳送SIGALRM訊號,功能比alarm更強大;
  • abort():向程序傳送SIGABORT訊號,預設程序會異常退出。
  • raise():用於向程序自身傳送訊號;

4.5 訊號相關函式

訊號集操作函式

  • sigemptyset(sigset_t *set):訊號集全部清0;
  • sigfillset(sigset_t *set): 訊號集全部置1,則訊號集包含linux支援的64種訊號;
  • sigaddset(sigset_t *set, int signum):向訊號集中加入signum訊號;
  • sigdelset(sigset_t *set, int signum):向訊號集中刪除signum訊號;
  • sigismember(const sigset_t *set, int signum):判定訊號signum是否存在訊號集中。

訊號阻塞函式

  • sigprocmask(int how, const sigset_t *set, sigset_t *oldset)); 不同how引數,實現不同功能
    • SIG_BLOCK:將set指向訊號集中的訊號,新增到程序阻塞訊號集;
    • SIG_UNBLOCK:將set指向訊號集中的訊號,從程序阻塞訊號集刪除;
    • SIG_SETMASK:將set指向訊號集中的訊號,設定成程序阻塞訊號集;
  • sigpending(sigset_t *set)):獲取已傳送到程序,卻被阻塞的所有訊號;
  • sigsuspend(const sigset_t *mask)):用mask代替程序的原有掩碼,並暫停程序執行,直到收到訊號再恢復原有掩碼並繼續執行程序。

轉:https://gityuan.com/2015/12/20/signal/

相關文章