訊號(或軟中斷)是在軟體層次上對中斷的乙個模擬,其執行在「使用者空間」,乙個程序對另外乙個或幾個程序通過傳送訊號來實現非同步通訊。當接收程序接收到訊號後,其可以註冊一下處理函式來說對這些訊號進行處理(也可以選擇忽略該訊號或者採用系統預設的處理方式)。我看可以通過「kill -l」命令來檢視系統支援的訊號,比如sigkill它表示需要終止乙個程序,它有乙個系統特定的訊號值9。這些值都定義在signal.h中
在signal.h中有個叫做_nsig(一般為64)的巨集其表示該系統支援的最多訊號數,而sigrtmax (_nsig-1
)則表示訊號的最大值,而與之相對應的sigrtmin卻不是表示訊號的最小值,其表示可靠訊號的最小值。按照訊號的可靠性,可將訊號分為「可靠訊號(實時訊號)」和「不可靠訊號(非實時訊號)」,以sigrtmin為界限,值小於sigrtmin的訊號為不可靠訊號,其繼承於早期的unix系統,sigrtmin到sigrtmax之間的為可靠訊號。
不可靠訊號有兩點需要注意:一是其在執行完自定義訊號處理函式後會將訊號處理方式重置為系統預設方式(如果要多次處理同乙個訊號,則要多次按照訊號處理函式,不過好像後期的linux對這點做了改進而無需重新安裝)。二是不可靠訊號不支援排隊,其有可能會出現訊號丟失的情況。
假設程序a向程序b傳送訊號,那麼乙個很簡單的訊號流程是:程序a呼叫某個函式產生某個訊號,訊號被傳送到程序b,然後程序b對該訊號進行處理。
訊號的產生和傳送:
我們用結構
typedef
struct
siginfo signifo_t;
來表示乙個訊號的相關資訊(比如訊號值,由誰傳送的等等)
用結構struct
sigqueue ;
來表示有n個siginfo_t結構構成的佇列。
再假設有這樣乙個資料結構:
typedef
struct
sigset_t;
其中_nsig_words表示的值一般為2。我們知道long為32位,那麼sig[_nsig_words]則為64位,其可以表示64個位的集合,很容易地就可以將集合中的元素其對映到_nsig個訊號上:如果該位為1則表示有著對應值的訊號在該集合中。(實際上,因為沒有0號訊號,所以訊號值和位值剛好錯開一位)
ok,我們如何用這些資料結構來表示哪些訊號被傳送到了某個程序呢,很簡單地,如果程序描述符(pcb)中有sigset_t型別的字段的話,將該字段的對應位置1就可以了,而這些訊號的詳細資訊如果能被儲存在sigqueue型別的字段中的話就更完美了。實際上pcb就是這麼做的,只不過其將sigset_t和sigqueue合併成了乙個稱為sigpending的結構:
struct
sigpending ;
所以pcb的sigpending欄位表示被傳送到了該程序但還沒有來得及處理的訊號(被掛起的)。
從這裡我們可以看出,所謂的「訊號的產生」實際上就是某個程序請求核心去將另外乙個程序的sigpending欄位設定成相應的值罷了,並將相應的其他資訊插入到sigqueue佇列中。
設定sigpending時有乙個比較有意思的地方:我們知道,可以用sigset_t中的某乙個位表示對應的訊號是否被傳送到了該程序,其只能簡單地表示「有(
1)」或「無(0
)」,如果某個訊號被傳送了多次的話,則無法在sigset_t中體現,但可以通過sigqueue佇列來體現,實際上,對應不可靠訊號(值小於sigrtmin的),當設定sigpending時,如果核心發現signal_t相應位已經被置1的話,則核心會丟棄該訊號,這也就是為什麼說該訊號是「不可靠的」;而對應可靠訊號,即便signal_t相應位已經被置1,此次訊號的相關資料(siginfo_t)仍然會被包裝成乙個佇列節點而被插入到佇列中去。
另外從程式設計角度,要傳送乙個訊號,一般呼叫下面這些函式:
int kill(pid_t pid, int
sig);
向程序或程序組傳送某個訊號
int raise(int
sig);
向呼叫程序自身發訊號
int sigqueue(pid_t pid, int signo, const
union sigval value);
與kill類似,但多了一些附加資訊
unsigned alarm(unsigned seconds);
指定的時間後向呼叫程序傳送乙個sigalrm訊號(鬧鐘訊號)
int setitimer(int which, const
struct itimerval *value, struct itimerval *ovalue);
與alarm類似同樣傳送sigalrm訊號,但支援更詳細的設定,更多參考這裡
void abort(void
);向程序傳送sigabrt訊號,讓程序中止。
訊號的接收和處理
程序從核心態返回使用者態時,核心會去檢查是否有訊號被傳送到了該程序,如果有,則讓該程序該處理該訊號。程序對於乙個訊號可以有三種處理方式:
1,顯示地忽略該訊號:
其中sigkill和sigstop是不能被忽略的,其必須採用下面的第2種方式。
2,採用系統預設的處理方式進行處理:
預設處理可以有這麼幾種方式:abort; abort並dump; ignore; stop(暫停程序,置為 task_stopped狀態);
continue
(繼續執行,與stop向對應,置為task_running狀態)
3,呼叫程序註冊的訊號處理函式進行自定義處理。
當處理完畢後,核心會改變sigpending中的相關值以表示處理完畢了(比如將sigset_t相應位置0,從sigqueue中刪除相關元素等)
至於如何註冊訊號處理函式,linux有下面兩種方式:
typedef
void (*sighandler_t)(int
); sighandler_t signal(
intsignum, sighandler_t handler);
呼叫signal()函式,其表示從現在開始我關心那些訊號值為signum的訊號,如果該訊號傳送到笨程序的話,請呼叫handler去處理它。其主要用於非可靠訊號。
比如下面這個demo,去自定義處理sigint (按ctrl-c終止程序時會傳送該訊號)
#include
#include
#include
#define sig2catch 2
void handler(int
signum)}
intmain()
printf(""
); return0;
}執行程式,並試圖用ctrl-c結束程式後,sigint(值為2)訊號會傳送到該程序,預設情況下其會終止程式,但這裡採用了自定義處理函式,其僅僅列印出一段文字(可以用ctrl-z結束)
另乙個註冊方法是:
int sigaction(int signum, const
struct sigaction *act, struct sigaction *oldact);
Linux程序訊號筆記總結
訊號 作用 為了通知我們某個事件的發生 就是乙個軟中斷,通知程序發生某件事情,打斷程序當前的操作,去先處理這個事件 必須認識訊號,訊號不是立即處理的。而實現記錄下來,選擇乙個合適的事機處理 訊號必須有預設的處理方式,當然也可以改變。訊號是可以被阻塞,暫時不處理 訊號的週期性 生命週期 產生 註冊 登...
Linux 程序訊號
概念 訊號是程序之間事件非同步通知的一種方式,屬於軟中斷。訊號處理常見方式 忽略此訊號。執行該訊號的預設處理動作。提供乙個訊號處理函式,要求核心在處理該訊號時切換到使用者態執行這個處理函式,這種方式稱為捕捉 catch 乙個訊號。產生訊號 捕捉訊號 核心如何實現訊號的捕捉 如果訊號的處理動作是使用者...
Linux程序訊號
訊號是乙個軟體中斷。作業系統通過訊號告訴程序發生了某個事件,打斷程序當前的操作,去處理這個事件。1.訊號的檢視kill l 檢視系統中的訊號種類在linux作業系統中,一共有62中訊號。34 64號訊號 後期擴充的,因為沒有具體對應事件,因此命名比較草率 可靠訊號。訊號的生命週期 產生 在程序中註冊...