訊號是軟體中斷。訊號提供非同步事件處理方法。
早期的訊號模型 不可靠,訊號可能丟失。posix.1對可靠訊號例程進行了標準化。
呼叫kill(2)函式可將任意訊號傳送給另乙個程序或程序組,但有限制:
當某個訊號出現時,核心有3種處理方式:
忽略:有2種訊號不能被忽略 -sigkill 和 sigstop, 原因是:它們向核心和超級使用者提供了使程序終止或停止的可靠方法;
捕捉:比如,若捕捉到sigchld訊號,則表示乙個子程序已經終止,所以sigchld訊號的捕捉函式可呼叫 waitpid 以取得該子程序的程序id和它的終止狀態。又比如,若程序建立了臨時檔案,那麼要為 sigterm 訊號編寫乙個訊號捕捉函式以清除臨時檔案(sigterm是終止訊號)
執行預設動作:圖10-1給出了每一種訊號的系統預設動作(p251)。大多數訊號的系統預設動作是終止該程序。
常見訊號及發生原因:
sigabrt
呼叫abort函式時產生此訊號;程序異常終止。
sigalrm
程序所設定的定時器超時
sigchld
當乙個程序終止或停止時,sigchld被送給其父程序;系統預設忽略此訊號;若父程序希望被告知,則其應該捕捉此訊號,在訊號處理函式中呼叫一種wait函式以取得子程序的id和終止狀態。
sigcont
這是作業控制訊號,傳送給需要繼續執行,但當前處於停止狀態的程序。若程序沒有處於停止狀態,則預設動作是忽略此訊號。
sigfpe
算術運算異常,如除以0、浮點移出
sighup
終端介面檢測到乙個連線斷開,則將此訊號傳送給與該終端相關的控制程序(即會話首程序)
sigint
中斷訊號,由ctrl+c產生,用來停止程式;
sigio
乙個非同步i/o事件產生了
sigkill
用來殺死程序,不能被忽略
sigpipe
在管道的讀程序已終止後,乙個程序寫此管道,則產生此訊號;
sigquit
ctrl+\ ,不僅終止前台程序組,而且產生乙個core檔案
sigse**
無效的記憶體訪問
sigstop
這是乙個作業控制訊號,停止乙個程序;不能被忽略
sigterm
一般讓該訊號的捕捉函式在程式退出之前做好清理工作,從而優雅地終止
sigtstp
互動停止訊號;當使用者在終端上按 ctrl+z 後,終端驅動程式產生此訊號。該訊號傳送給前台程序組的所有程序。
#include void (*signal(int signo, void(*func)(int)))(int);
這個原型太複雜了,使用下面的typedef會簡單一些:
typedef void sigfunc(int);
sigfunc *signal(int, sigfunc *);
簡單來講,signal函式原型是:
檢視系統的標頭檔案signal.h,可以看到下列形式的宣告:
#define sig_err (void (*)())-1
#define sig_dfl (void (*)())0
#define sig_ign (void (*)())1
這些常量用於表示「指向函式的指標,該函式要求乙個整型引數,而無返回值」。這些常量所用的3個值不一定是-1、0、1,但它們必須是3個值而決不能是任一函式的位址。
程式示例:
#include // one handler for 2 signals
static void sig_user(int)
int main()
return 0;
}
程式的啟動
exec函式將原先設定為要捕捉的訊號都更改為預設動作,其他訊號的狀態則不變(乙個程序原先要捕捉的訊號,當其執行乙個新程式後,就不能再捕捉了,因為訊號捕捉函式的位址一般在所執行的新程式檔案中已無意義)。
當程序呼叫fork後,子程序複製了父程序的記憶體映像,所以訊號捕捉函式的位址在子程序中是有意義的。
很多捕捉到sig_int和sig_ign的程式具有下列形式的**:
void sig_int(int);
void sig_quit(int);
if (signal(sigint, sig_ign) != sig_ign) // 若當前未被忽略,才會捕捉
signal(sigint, sig_int);
if (signal(sigquit, sig_ign) != sig_ign) // 若當前未被忽略,才會捕捉
signal(sigquit, sig_quit);
不可靠指的是,訊號可能會丟失。
有時候使用者希望通知核心阻塞某個訊號,即:不忽略該訊號,在其發生時記住它,在程序做好了準備時再通知它。這種阻塞訊號的能力,在早期並不具備。
早期的unix系統:如果程序在執行乙個低速系統呼叫而阻塞時,捕捉到乙個訊號,則該系統呼叫就被中斷而不再執行。
後來的unix系統:捕捉到訊號並處理後,會返回已讀或已寫的部分,從而視該系統呼叫為成功。
4.2bsd引入了一些被中斷後可以自動重啟動的系統呼叫:ioctl、read、readv、write、writev、wait、waitpid.
前5個函式只有對低速裝置操作時才會被訊號中斷,而wait和waitpid在捕捉到訊號時則總是被中斷。
4.3bsd允許程序基於每個訊號禁用此自動重啟動的功能。
系統呼叫可分為2類:低速系統呼叫和其他系統呼叫。
低速系統呼叫是可能會使程序永遠阻塞的一類系統呼叫。如,讀管道、終端、網路裝置時,資料不存在。
下列是一些不可重入函式的特徵:
使用了靜態的資料結構
呼叫了malloc或free
是標準i/o函式,因為標準i/o庫的很多實現都以不可重入的方式使用全域性資料結構
作為一種通用的規則,當在訊號處理程式中呼叫圖10-4中的這些可重入函式時,應當在呼叫前儲存errno,在呼叫後恢復errno.
在訊號處理程式中,呼叫乙個不可重入函式,其結果是不可預知的。舉個栗子,當主程式中呼叫free時被訊號中斷,而訊號處理程式中也呼叫了free,那麼malloc和free維護的資料結構就遭到了破壞,從而程式會執行出錯。
sigcld是systemv的乙個訊號名,其語義與名為sigchld的bsd訊號不同。posix.1採用bsd的sigchld訊號。
bsd的sigchld的語義是,子程序改變狀態後產生此訊號,父程序需要呼叫乙個wait函式以檢測發生了什麼。
對於sigcld的處理方式(略)
注意,linux3.2.0和solaris 10定義了sigcld,其等同於sigchld.
當乙個訊號產生時,核心通常在程序表中以某種形式設定乙個標誌。當核心做這個動作時,我們稱為「向程序遞送了乙個訊號」。
在訊號產生(generation)和遞送(delivery)之間的時間間隔,稱訊號是未決的(pending)。
程序可以「阻塞訊號遞送」。如果為程序產生了乙個阻塞的訊號,而且訊號處理動作是系統預設動作(sig_dfl)或捕捉該訊號,那麼該程序將此訊號保持為未決狀態,直到該程序對此訊號解除了阻塞或對此訊號的動作更改為忽略。
程序呼叫sigpending 函式來判定哪些訊號是設定為阻塞並處於未決狀態的。
如果在程序解除對某個訊號的阻塞之前,該訊號發生了多次,將如何呢?
posix.1允許系統遞送該訊號一次或多次。若遞送多次,則稱這些訊號排隊了。但除非支援posix.1的實時擴充套件,否則大多數unix並不對訊號排隊,而只遞送一次。
若有多個訊號要遞送給乙個程序,posix.1並沒有規定這些訊號的遞送順序。但posix.1基礎部分建議:在其他訊號之前遞送與程序當前狀態有關的訊號,如sigse**.
每個程序都有乙個訊號遮蔽字(signal mask),它規定了當前要阻塞而不遞送到該程序的訊號集。對於每種可能的訊號,該遮蔽字中都有一位與之對應。對於某種訊號,若其對應位已經設定,則它當前是被阻塞的。程序可以呼叫sigprocmask 函式來檢測和更改其當前的訊號遮蔽字。
訊號編號可能會超過乙個整型的二進位制位數,因此posix.1定義了乙個新資料型別sigset_t, 它可以容納乙個訊號集。
資料型別訊號集(signal_set)被函式sigprocmask用於告訴核心不允許發生在該訊號集中的訊號。
下面是處理訊號集的函式
#include int sigemptyset(sigset_t *set); // 清除訊號集中的所有訊號
int sigfillset(sigset_t *set); // 初始化由set指向的訊號集,使其包括所有訊號
int sigaddset(sigset_t *set, int signo); // 講乙個訊號signo新增到訊號集set中
int sigdelset(sigset_t *set, int signo); // 從訊號集中刪除乙個訊號
// 以上4個函式,若成功,返回0,失敗返回-1
int sigismember(const sigset_t * set, int signo); // 判斷訊號signo是否在訊號集set內
posix.1認為有以下6個與作業控制有關的訊號:
其他略略
(完)
APUE讀書筆記 第10章 訊號
第10章 訊號 10.1 引言 訊號是軟體中斷。訊號提供了一種處理非同步事件的方法 10.2 訊號概念 每個訊號都有乙個名字。這些名字都以三個字元sig開頭 在標頭檔案中,這些訊號被定義為正整數 訊號編號 不存在編號為0的訊號。kill函式對訊號編號0有特殊的應用。此種訊號編號值被稱為空訊號 10....
APUE學習筆記 10 訊號概念
by 潘雲登 對於商業目的下對本文的任何行為需經作者同意。寫在前面 1.本文內容對應 unix 環境高階程式設計 第 2版 第 10章。2.總結了有關訊號的基本概念,包括訊號產生的原因和對訊號的處理方式。3.訊號概念 訊號是軟體中斷,提供了一種處理非同步事件的方法。每個訊號都有乙個名字,以字元 si...
APUE第十章 訊號
同步 乙個程序在執行某個請求的時候,若該請求需要一段時間才能返回資訊,那麼這個程序將會一直等待下去,直到搜到返回資訊才繼續執行。非同步 乙個程序在執行某個請求的時候,不需要一直等下去,而是繼續執行之後的操作,有訊息返回時系統會通知程序進行處理。同步和非同步關注的是程序之間的訊息通訊機制,區別阻塞和非...