1. 程式人生 > 其它 >20191302第六章學習筆記

20191302第六章學習筆記

第六章 訊號與訊號處理

本章概述

  • 本章講述了訊號和訊號處理;
  • 介紹了訊號和中斷的統一處理,有助於從正確的角度看待訊號;將訊號視為程序中斷,將程序從正常執行轉移到訊號處理;
  • 解釋了訊號的來源,包括來自硬體、異常和其他程序的訊號;
  • 然後舉例說明了訊號在Unix/Linux 中的常見用法;
  • 詳細解釋了Unix/Linux中的訊號處理,包括訊號型別、訊號向量位、訊號掩碼位、程序PROC結構體中的訊號處理程式以及訊號處理步驟;
  • 用示例展示瞭如何安裝訊號捕捉器來處理程式異常,如使用者模式下的段錯誤;
  • 還討論了將訊號用作程序間通訊(IPC)機制的適用性。讀者可藉助該程式設計專案,使用訊號和管道來實現用於程序交換資訊的程序間通訊機制。

訊號和中斷

  • “中斷”是從I/O裝置或協處理器傳送到CPU的外部請求,它將CPU從正常執行轉移 到中斷處理。與傳送給CPU的中斷請求一樣,“訊號”是傳送給程序的請求,將程序從正常執行轉移到中斷處理。
  • 程序:一個“程序”就是一系列活動。廣義的 “程序”包括:從事日常事務的人。在使用者模式或核心模式下執行的Unix/Linux程序。執行機器指令的CPU。
  • “中斷”是傳送給“程序”的事件,它將“程序”從正常活動轉移到其他活動,稱為“中斷處理”。“程序”可在完成“中斷”處理後恢復正常活動。
  • 根據來源,中斷可分為三類:
    • 來自硬體的中斷;
    • 來自其他人的中斷;
    • 自己造成的中斷。
  • 按照緊急程度,中斷可分為以下幾類:
    • 不可遮蔽(NMI);
    • 可遮蔽。
  • 程序中斷
    • 這類中斷是傳送給程序的中斷。當某程序正在執行時,可能會收到來自3個不同來源的中斷:
    • 來自硬體的中斷:終端、間隔定時器的“Ctrl+C”組合鍵等。
    • 來自其他程序的中斷:kill(pid,SIG#), death_of_child等。
    • 自己造成的中斷:除以0、無效地址等。
    • 每個程序中斷都被轉換為一個唯一ID號,傳送給程序。與多種類的人員中斷不同,我們始終可限制在一個程序中的中斷的數量。
    • Unix/Linux中的程序中斷稱為訊號,編號為1到31。
    • 程序的PROC結構體中有對應每個訊號的動作函式,程序可在收到訊號後執行該動作函式。
    • 與人員類似,程序也可遮蔽某些型別的訊號,以推遲處理。必要時,程序還可能會修改訊號動作函式。
  • 硬體中斷:
    • 這類中斷是傳送給處理器或CPU的訊號。它們也有三個可能的來源:
      • 來自硬體的中斷:定時器、I/O裝置等.
      • 來自其他處理器的中斷:FFP. DMA、多處理器系統中的其他CPU。
      • 自己造成的中斷:除以0、保護錯誤、INT指令。
  • 毎箇中斷都有唯一的中斷向量號。動作函式是中斷向量表中的中斷處理程式。
  • 程序的陷阱錯誤
    • 程序可能會自己造成中斷。這些中斷是由被CPU識別為異常的錯誤引起的,例如除以0、無效地址、非法指令、越權等。
    • 當程序遇到異常時,它會陷入作業系統核心,將陷阱原因轉換為訊號編號,並將訊號傳送給自己。如果在使用者模式下發生異常,則程序的預設操作是終止,並使用一個可選的記憶體轉儲進行除錯。

Unix/Linux中的訊號處理

  • Unix/Linux支援31種不同的訊號,每種訊號在 signal.h檔案中都有定義。
#define SIGHUP
#define SIGINT
#define SIGQUIT
#define SIGILL #define SIGTRAP
#define SIGABRT #define SIGIOT
#define SIGBUS
#define SIGFPE
#define SIGKILL
#define SIGUSR1
#define SIGSEGV
#define SIGUSR2
#define SIGPIPE #define SIGALRM
#define SIGTERM
#define SIGSTKFLT
#define SIGCHLD
#define SIGCONT
#define SIGSTOP
#define SIGTSTP
#define SIGTTIN
#define SIGTTOU
#define SIGURG
#define SIGXCPU
#define SIGXFSZ
#define SIGVTALRM
#define SIGPROF
#define SIGWINCH
#define SIGPOLL
#define SIGPWR
#define SIGSYS

Unix/Linux訊號示例

  • (1)按“Ctrl+C”組合鍵通常會導致當前執行的程序終止。原因如下:
    • “Ctr1+C”組合鍵會生成一個鍵盤硬體中斷。鍵盤中斷處理程式將“Ctrl+C”組合鍵轉換為SIGINT(2)訊號,傳送給終端上的所有程序,並喚醒等待鍵盤輸人的程序。在核心模式下,每個程序都要檢查和處理未完成的訊號。程序對大多數訊號的預設操作是呼叫核心的kexit(exitValue)函式來終止。在Linux中,exitValue的低位位元組是導致程序終止的訊號編號。
  • (2)使用者可使用nohup a.out 命令在後臺執行一個程式。即使在使用者退出後,程序仍將繼續執行。
    • nobup命令會使sh像往常一樣復刻子程序來執行程式,但是子程序會忽略SIGHuP(1)訊號。當用戶退出時,sh會向與終端有關的所有程序傳送一個SIGHUP訊號。後臺程序在接收到這一訊號後,會忽略它並繼續執行。為防止後臺程序使用終端進行I/O,後臺程序通常會斷開與終端的連線(通過將其檔案描述符0、1、2重定向到/dev/null),使其完全不受任何面向終端訊號的影響。
  • (3) 使用者可以使用sh命令killpid(orkill-s9pia)殺死該程序。方法如下。
    • 執行殺死的程序向pid標識的目標程序傳送一個SIGTERM ( 15 )訊號,請求它死亡。目標程序將會遵從請求並終止。如果程序選擇忽略SIGTERM訊號,它可能拒絕死亡。

訊號的來源

  • 來自硬體中斷的訊號:在執行過程中,一些硬體中斷被轉換為訊號傳送給程序硬體訊號示例。
  • 中斷鍵(Ctrl+C),它產生一個SIGINT(2)訊號。
  • 間隔定時器,當他的時間到期時,會生成一個SIGALRM(14)、SIGTALRM(26)或SIGPROF(27)訊號。
  • 其他硬體錯誤,如匯流排錯誤、IO陷進。
  • 來自異常的訊號:常見的陷阱訊號有SIGFPE(8),表示浮點異常(除以0),最常見也是最可怕的時SIGSEGV(11),表示段錯誤。
  • 來自其他程序的訊號:程序可以使用kill(pid,sig)系統呼叫向pid標識的目標程序傳送訊號。

訊號處理函式

  • 每個程序PROC 都有一個訊號處理陣列 int sig[32]。Sig[32]陣列的每個條目都指定了如何處理相應的訊號,其中0表示 DEFault(預設).1表示 IGNore(忽略).其他非零值表示使用者模式下預先安裝的訊號捕捉(處理)函式。下圖給出了訊號位向量、遮蔽位向量和訊號處理函式。

實踐

#include<stdio.h>
#include<stdlib.h> 
#include<unistd.h> 
#include<signal.h> 
#include<setjmp.h>
#include<string.h>
jmp_buf env;
int count = 0;
void handler(int sig, siginfo_t *siginfo, void *context)
{
printf ("handler: sig=%d from PID=%d UID=%d count=%d\n",
	       	sig, siginfo->si_pid, siginfo->si_uid, ++count); 
	if (count >= 4) // let it occur up to 4 times
		longjmp(env, 1234);
}
int BAD()
{
int *ip = 0;
printf("in BAD(): try to dereference NULL pointer\n");
*ip = 123;	// dereference a NULL pointer
printf("should not see this line\n");
}
int main (int argc, char *argv[])
{
int r;
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = &handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV,&act,NULL);
if ((r = setjmp(env)) == 0)
	BAD();
else
	printf("proc %d survived SEGMENTATION FAULT: r=%d\n",getpid(), r);

printf("proc %d looping\n",getpid());
while(1);
}

執行結果