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

第六章 學習筆記

第六章 訊號和訊號處理 學習筆記

一、訊號和中斷

中斷:是從I/O裝置或協處理器傳送到CPU的外部請求,它將CPU從正常執行轉移到中斷處理。
訊號:發給程序的請求,將程序從正常執行轉移到中斷處理。
終端主要有以下幾種型別

  • 人員中斷

  • 程序中斷

  • 硬體中斷

  • 程序的陷阱錯誤

二、Unix/Linux訊號示例

(1)按Ctrl+C組合鍵通常會導致當前執行的程序終止。原因如下:Ctrl+C組合鍵會生成一個鍵盤硬體中斷。鍵盤中斷處理程式將Ctrl+C組合鍵轉換為SIGINT (2)訊號,傳送給終端上的所有程序,並喚醒等待鍵盤輸入的程序。在核心模式下,每個程序都要檢查和處理未完成的訊號。程序對大多數訊號的預設操作是呼叫核心的kexit(exitValue)函式來終止。在Linux中,exitValue的低位位元組是導致程序終止的訊號編號。
(2)使用者可使用nohup a.out &命令在後臺執行一個程式。即使在使用者退出後,程序仍將繼續執行。
(3)使用者再次登入時也許會發現(通過ps-u LTD)後臺程序仍在執行。使用者可以使用sh命令kill pid (or kill -s 9 pid)殺死該程序。

三、Unix/Linux中的訊號處理

1.訊號型別

Unix/Linux支援31種不同的訊號,每種訊號在signal.h檔案中都有定義

2.訊號的來源

有如下三種:
來自硬體中斷的訊號:在程序執行過程中,一些硬體中斷被轉換為訊號傳送給程序。
來自異常的訊號:當用戶模式下的程序遇到異常時,會陷入核心模式,生成一個訊號,併發送給自己。
來自其他程序的訊號:程序可使用kill(pid, sig)系統呼叫向pid標識的目標程序傳送訊號。

3.程序PROC結構體中的訊號

每個程序PROC都有一個32位的向量,用來記錄傳送給程序的訊號。在位向量中,每一位代表一個訊號編號。此外,他還有一個訊號MASK位向量,用來遮蔽相應的訊號。

4.訊號處理函式

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

如果訊號位向量中的位I為1,則會生成一個訊號I或將其傳送給程序。如果遮蔽位向量的位I為1,則訊號會被阻塞或遮蔽。否則,訊號未被阻塞。只有當訊號存在並且未被阻塞時,訊號才會生效或傳遞給程序。

5.安裝訊號捕捉函式

程序可使用系統呼叫:int r = signal(int signal_numberr void *handler);來修改選定訊號編號的處理函式,SIGK1LL (9)和SIGSTOP (19)除外,它們不能修改。signal()系統呼叫在所有類Unix系統中均可用,但它有一些缺點:

1.在執行已安裝的訊號捕捉函式之前,通常將訊號處理函式重置為DEFault。為捕捉下次出現的相同訊號,必須重新安裝捕捉函式。
2.signal()不能阻塞其他訊號。
3.signal()只能向捕捉函式傳送一個訊號編號。
4.signal()可能不適用於多執行緒程式中的執行緒。
5.不同Unix版本的signal。可能會有所不同。

signal()已經被sigaction()函式所代替,它的原型是int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);,sigaction結構體的定義為

其中重要的欄位如下:

sa_handler :該欄位是指向處理函式的指標,該函式與signal()的處理函式有相同的原型。
sa_sigaction:該欄位是執行訊號處理函式的另一種方法。它的訊號編號旁邊有兩個額外引數,其中siginfo t *提供關於所接收訊號的更多資訊。
sa_mask:可在處理函式執行期間設定要阻塞的訊號。
sa_flags :可修改訊號處理程序的行為。若要使用sa_sigaction處理函式,必須將sa_flags設定為SA_SIGINFO。

七、Linux中的IPC

IPC是指用於程序間通訊的機制。在Linux中,IPC包含以下組成部分:

(1)管道和FIFO
一個管道有一個讀取端和一個寫入端。管道的主要用途是連線一對管道寫程序和讀程序。管道寫程序可將資料寫入管道,讀程序可從管道中讀取資料。管道控制機制要對管道讀寫操作進行同步控制。未命名管道供相關程序 使用,命名管道是FIFO的,可供不相關程序使用。在Linux中的管道讀取操作為同步和阻塞。如果管道仍有寫程序但沒有資料,讀程序會進行等待。

(2)訊號
程序可使用kill系統呼叫向其他程序傳送訊號.其他程序使用訊號捕捉函式處理訊號,將訊號用作IPC的一個主要缺點是訊號只是用作通知,不含任何資訊內容。

(3)System V IPC
包括共享記憶體、訊號址和訊息佇列。在Linux中,多種 System V 1PC函式,例如用於新增/移除共享記憶體的shmat/shmdt、用於獲取/操作訊號反的semget/semop和用於傳送/接收訊息的msgsnd/msgrcv,都是庫包裝函式,它們都會向 Linux核心發出一個ipc()系統呼叫。ipc()的實現是Linux所特有的,不可移植。

(4)POSIX訊息佇列
(5)執行緒同步機制
程序是共享某些公共資源的執行緒。如果是使用有共享地址空間的clone()系統呼叫建立的程序,它們可使用互斥量和條件變數通過共享記憶體進行同步通訊。另外,常規程序可新增到共享記憶體,使它們可作為執行緒進行同步。

(6)套接字
用於跨網路程序通訊的IPC機制。

八、程式設計例項

根據書上的程式碼進行實踐:

#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);
}   

執行結果:

``