UNIX訊號(signal)程式設計 - UNIX高階環境程式設計第10章讀書筆記
其他章節
使用pthread庫進行多執行緒程式設計1 - UNIX環境高階程式設計第11章讀書筆記
使用pthread庫進行多執行緒程式設計2 - UNIX高階環境程式設計第12章讀書筆記
10 Signals
1 Introduction & Concepts
Signals是一種軟體中斷,通知程式某種事件的發生。常見的Signal有SIGABRT(當程式呼叫abort函式的時候自動傳送), SIGALRM(當timer被觸發的時候自動傳送),等等。
下面的情況可以產生Signal:
1. 按下CTRL+C產生SIGINT
2. 硬體中斷,如除0,非法記憶體訪問(SIGSEV)等等
3. Kill函式可以對程式傳送Signal
4. Kill命令。實際上是對Kill函式的一個包裝
5. 軟體中斷。如當Alarm Clock超時(SIGURG),當Reader中止之後又向管道寫資料(SIGPIPE),等等
當Signal發生的時候,可以有三種選擇(稱之為Disposition of the signal或者Action associated with a signal)
1. 忽略Signal。大部分Signal都可以Ignore,除了SIGKILL和SIGSTOP,這是為了提供一個確定的方法來Stop或者Kill一個Process。此外,如果我們忽略部分硬體異常產生的Signal,程式的行為未定義。
2. 捕捉Signal。可以讓核心來呼叫我們所指定的函式。SIGKILL和SIGSTOP無法捕捉。
3. 執行預設行為。如果不做任何處理,則執行預設動作。大部分Signal的預設行為都是中止程式。
部分Signal的預設行為不僅中止程式,同時還會產生core dump,也就是生成一個名為core的檔案,其中儲存了退出時程式記憶體的映象,可以用來除錯。在下面情況,不會生成core檔案:
1. 當前程式不屬於當前user
2. 當前程式不屬於當前group
3. 使用者在當前目錄下無寫許可權
4. Core檔案已存在,使用者無寫許可權
5. 檔案過大,超過RLIMIT_CORE
2 Signals
Signal |
Description |
SIGABRT |
由呼叫abort函式產生,程式非正常退出 |
SIGALRM |
用alarm函式設定的timer超時或setitimer函式設定的interval timer超時 |
SIGBUS |
某種特定的硬體異常,通常由記憶體訪問引起 |
SIGCANCEL |
由Solaris Thread Library內部使用,通常不會使用 |
SIGCHLD |
程式Terminate或Stop的時候,SIGCHLD會傳送給它的父程式。預設情況下該Signal會被忽略 |
SIGCONT |
當被stop的程式恢復執行的時候,自動傳送 |
SIGEMT |
和實現相關的硬體異常 |
SIGFPE |
數學相關的異常,如被0除,浮點溢位,等等 |
SIGFREEZE |
Solaris專用,Hiberate或者Suspended時候傳送 |
SIGHUP |
傳送給具有Terminal的Controlling Process,當terminal被disconnect時候傳送 |
SIGILL |
非法指令異常 |
SIGINFO |
BSD signal。由Status Key產生,通常是CTRL+T。傳送給所有Foreground Group的程式 |
SIGINT |
由Interrupt Key產生,通常是CTRL+C或者DELETE。傳送給所有ForeGround Group的程式 |
SIGIO |
非同步IO事件 |
SIGIOT |
實現相關的硬體異常,一般對應SIGABRT |
SIGKILL |
無法處理和忽略。中止某個程式 |
SIGLWP |
由Solaris Thread Libray內部使用 |
SIGPIPE |
在reader中止之後寫Pipe的時候傳送 |
SIGPOLL |
當某個事件傳送給Pollable Device的時候傳送 |
SIGPROF |
Setitimer指定的Profiling Interval Timer所產生 |
SIGPWR |
和系統相關。和UPS相關。 |
SIGQUIT |
輸入Quit Key的時候(CTRL+/)傳送給所有Foreground Group的程式 |
SIGSEGV |
非法記憶體訪問 |
SIGSTKFLT |
Linux專用,數學協處理器的棧異常 |
SIGSTOP |
中止程式。無法處理和忽略。 |
SIGSYS |
非法系統呼叫 |
SIGTERM |
請求中止程式,kill命令預設傳送 |
SIGTHAW |
Solaris專用,從Suspend恢復時候傳送 |
SIGTRAP |
實現相關的硬體異常。一般是除錯異常 |
SIGTSTP |
Suspend Key,一般是Ctrl+Z。傳送給所有Foreground Group的程式 |
SIGTTIN |
當Background Group的程式嘗試讀取Terminal的時候傳送 |
SIGTTOU |
當Background Group的程式嘗試寫Terminal的時候傳送 |
SIGURG |
當out-of-band data接收的時候可能傳送 |
SIGUSR1 |
使用者自定義signal 1 |
SIGUSR2 |
使用者自定義signal 2 |
SIGVTALRM |
setitimer函式設定的Virtual Interval Timer超時的時候 |
SIGWAITING |
Solaris Thread Library內部實現專用 |
SIGWINCH |
當Terminal的視窗大小改變的時候,傳送給Foreground Group的所有程式 |
SIGXCPU |
當CPU時間限制超時的時候 |
SIGXFSZ |
程式超過檔案大小限制 |
SIGXRES |
Solaris專用,程式超過資源限制的時候傳送 |
3 Signal Function
#include <signal.h> Void (*signal(int signo, void (*func)(int)))(int); 返回之前的signal處理函式,錯誤返回SIG_ERR |
1. Signo:signal的名稱
2. Func:函式地址,或者是SIG_IGN(忽略Signal)或SIG_DFL(預設行為)。原型為:void (*)(int)。不過很多UNIX的實現也會傳入一些和實現相關的引數
如果用exec建立子程式,那麼子程式的所有Signal狀態是預設或者忽略:
1. 如果父程式忽略了某個Signal,那麼父程式用exec建立子程式時子程式的這個Signal的狀態也是忽略
2. 如果在父程式的某個Signal註冊了Signal處理函式的話,那麼這個子程式的該Signal的狀態會恢復成預設狀態,因為註冊的函式地址肯定在另外一個程式中無法呼叫。
如果用fork建立子程式,那麼子程式會繼承所有父程式的Signal狀態。
4 Unreliable Signals
早期的UNIX系統的Signal是不穩定的:
1. Signal可能會Lost
2. 在signal處理函式中,需要重複註冊signal,否則下次收不到signal
3. 不支援pending signal
5 Interrupted System Calls
1. 早期UNIX中,一個程式在呼叫系統呼叫被阻賽的時候, 有可能被一個Signal中斷。此時系統呼叫返回錯誤並且errno被設定為EINTR。
2. 系統呼叫也被分為兩類:slow和一般。slow系統呼叫是那些可能永遠block的系統呼叫(disk IO是例外,一般情況下只會暫時Block,但也歸入Slow一類)
3. 為了處理這個問題,在早期UNIX系統中對於這些系統呼叫需要作額外的檢查和判斷,如果錯誤並且errno=EINTR,需要重新呼叫
4. Free BSD 4.2對於部分系統呼叫ioctl, read, readv, write, writev, wait實現了自動重新恢復功能,也就是當函式被中斷的時候會自動恢復到原來的執行情況
5. Free BSD 5.2.1, Linux 2.4.22, Mac OS X 10.3支援此功能,但POSIX.1不作強制要求
6 Reentrant Functions
在Signal處理函式中,有可能在執行某個函式的時候,signal產生並呼叫了signal處理函式,然後再signal處理函式中再次呼叫了此函式,如果此函式呼叫了malloc/free,或者使用了static變數等技巧,會造成此函式不可再次被呼叫,此函式就是不可重入,反之就是可以重入的。UNIX標準定義了一個表,表中的函式都是可重入的。
7 Reliable Signal Terminology and Semantics
1. 在一個Signal被Generate之後,而被Deliver之前,該Signal稱為正處於pending狀態
2. 程式可以Block某個Signal。如果某個被Block的signal產生的話,該signal一直會處於pending狀態,直到程式
a. Unblock這個Signal
b. 改變Signal的狀態為Ignore
3. 如果一個被Block的Signal產生多次,POSIX.1允許deliver該signal一次或者多次
4. 每個程式有一個signal mask,表示那些signal被Block,那些被允許
5. sigset_t用來表示多種signal的集合
8 kill and raise Functions
Kill函式對某個程式傳送signal,而raise對本程式傳送signal:
#include <signal.h> int kill(pit_t pid, int signo); int raise(int signo) 成功返回0,錯誤返回-1 |
對於pid可以有4種可能性:
1. pid > 0:傳送給某個程式
2. pid == 0:傳送給所有group ID=當前傳送程式的group id,並且有許可權傳送。注意部分系統程式(具體是那些的話和具體實現相關)不包括在內
3. pid < 0:傳送給所有group ID= abs(pid)的程式,不包括部分系統程式
4. pid == -1:傳送給所有有許可權傳送signal的程式,不包括部分系統程式
順便說一句,我認為這個design不太好,最好設計成四個不同函式。
Superuser可以傳送signal給任何程式,而對於一般使用者來說,傳送者的user id必須等於接收者的user id
如果signo=0,則是告訴kill函式檢查該程式是否存在。如果不存在,則返回錯誤-1,errno設定為ESRCH。
9 alarm and pause Functions
Alarm函式設定一個timer,到固定時間之後會expire,傳送一個SIGALRM signal:
#include <signal.h> unsigned int alarm(unsigned int second); 返回前一個alarm的剩餘時間值,單位為秒 |
SIGALRM的預設行為是中止程式。同一個程式只能有一個alarm。呼叫alarm函式會取消前一個alarm,並返回前一個alarm的剩餘時間。如果second值為0,則只是取消前一個alarm。
Pause函式暫停當前程式的執行,直到某個signal被catch:
#include <signal.h> int pause(void); 總是返回-1,errno總是設定為EINTR |
10 signal sets
多個signal的集合用signal set表示,型別為sigset_t,下面函式用於操作sigset_t:
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); 成功返回0,錯誤返回-1 int sigismember(sigset_t *set, int signo); 返回1如果是,不是則返回0 |
1. sigemptyset:初始化sigset_t,所有signal都不包括在內
2. sigfillset:初始化sigset_t,包括所有signal
3. sigaddset, sigdelset:新增/刪除signal
4. sigismember:檢查signal set中是否包括該signal
11 sigprocmask Function
sigprocmask函式設定程式的signal mask,可以block/unblock signal:
#include <signal.h> int sigprocmask(int how, const sigset_t *restrict set, sig_set *restrict oset); 成功返回0,錯誤返回-1 |
1. how引數的意義如下:
How |
Description |
SIG_BLOCK |
在原來的mask基礎上Block signal set |
SIG_UNBLOCK |
在原來的mask基礎上Unblock signal set |
SIG_SETMASK |
設定mask為指定的signal set |
2. set:signal set,如果=NULL則忽略how引數(again,又是一個不太合適的設計,不利於差錯和記憶)
3. oset:返回當前process的signal mask
12 sigpending Function
Sigpending函式返回當前被block而沒有deliver的signal的集合:
#include <signal.h> int sigpending(sigset_t *set); 成功返回0,錯誤返回-1 |
13 sigaction Function
sigaction用於修改或者獲取某個signal所對應的action。Sigaction可以取代Signal函式,實際上,signal函式可以用sigaction函式來實現。。sigaction函式原型如下:
#include <signal.h> int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); 成功返回0,錯誤返回-1 |
1. signo:signal
2. act:對應的action
3. oact:之前的action,可以為NULL
sigaction如下:
struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int, siginfo_t *, void *) }; |
1. sa_handler:signal處理函式
2. sa_mask:當signal函式被呼叫的時候,自動block這些signal。當signal處理函式返回的時候,mask會恢復到初始值。同時,作業系統也會自動把該signal加到mask中,防止重入
3. sa_flags:定義如下:
Option |
Description |
SA_INTERRUPT |
被該signal中斷的系統呼叫不會自動重啟 |
SA_NOCLDSTOP |
如果signo是SIGCHLD,當子程式stop的時候不產生該signal |
SA_NOCLDWAIT |
如果signo是SIGCHLD,子程式不會變成zombie。當呼叫wait的時候,呼叫程式會一直等待直到所有子程式結束 |
SA_NODEFER |
在signal處理函式中系統不會自動block該signal |
SA_ONSTACK |
如果呼叫了sigaltstack指定一個額外的stack,那麼該signal傳送的時候,程式處於此stack中 |
SA_RESETHAND |
恢復為初始化值,SA_SIGINFO flag也會被清除 |
SA_RESTART |
被該signal中斷的系統呼叫會自動重啟 |
SA_SIGINFO |
為signal處理函式提供額外資訊 |
4. sa_sigaction:當sa_flags為SA_SIGINFO時,signal處理函式為sa_sigaction,接收額外兩個引數:siginfo_t和void *
siginfo_t表示該signal為何產生:
struct siginfo_t { int si_signo; /* signal */ int si_errno; /* errrno */ int si_code; /* 額外資訊,和具體signal相關 */ pid_t si_pid; /* 傳送signal程式的pid */ uid_t si_uid; /* 傳送signal的程式的uid */ void *si_addr; /* 產生fault(SIG_SEGV)的地址 */ int si_status; /* 退出值或者signal數值 */ long si_band; /* SIGPOLL的band數值 */ }; |
而void *則應該被轉換成ucontext_t結構,表示當signal產生時候的程式上下文資訊
14 sigsetjmp and siglongjmp Functions
1. setjmp,longjmp的函式的作用是,setjmp記住位置,然後longjmp可以自動跳轉到該位置,並且從setjmp函式返回
2. 當呼叫signal處理函式的時候,該signal會自動被block,如果這個時候我們呼叫longjmp跳出signal處理函式,那麼signal還會被Block嗎?答案是不確定,和具體實現相關
3. 為了解決這個問題,系統提供了sigsetjmp和siglongjmp。區別在於,如果sigsetjmp的savemask為非0,則sigsetjmp會在env引數中記住當前process的signal mask。一般情況下如果和signal打交道的話都需要呼叫這兩個函式。這兩個函式原型如下:
#include <signal.h> int sigsetjmp(sigjmp_buf env, int savemask); 直接呼叫返回0,從siglongjmp返回的時候返回非0 void siglongjmp(sigjmp_buf env, int val); |
15 sigsuspend Function
Sigsuspend函式可以在一個原子操作內設定mask然後pause,防止設定mask之後pause之前,signal handler被呼叫,程式永遠等待。原型如下:
#include <signal.h> int sigsuspend(sigset_t *sig); 總是返回-1,errno設定為EINTR |
16 abort Function
Abort函式等價於raise (SIGABRT)。注意就算是signal處理函式處理了SIGABRT,程式仍然會結束。
17 system Function
System函式除了會執行可執行檔案建立子程式之外,還會設定sigmask為忽略SIGINT,SIGQUIT並Block SIGCHLD。
1. 忽略SIGINT和SIGQUIT的原因是,只有子程式應該處理這兩個signal,而父程式無需處理
2. Block SIGCHLD的原因是,System函式需要知道子程式的結束,而父程式不應該先提前知道,以擴音前呼叫wait函式使得System函式本身無法獲得程式的退出值
18 sleep Function
Sleep函式可以sleep某段時間,精確性無法保證。程式睡眠指定時間,直到時間到或者signal產生並被handler處理。注意sleep可能會由alarm函式實現,所以把alarm函式和sleep函式混著使用可能會造成無法預料的結果。
19 Job-Control Signals
SIGCHLD, SIGCONT, SIGSTOP, SITTSTP, SIGTTIN, SITTOU被認為是和Job Control有關的signal。大部分情況下,程式無需處理這些signal,除了SIGCHLD之外。
20 Additional Features
有些系統提供sys_siglist陣列,儲存著每個signal對應的字串描述。
extern char *sys_siglist[]; |
很多系統提供psignal函式輸出一個msg + ‘: ’ + signal string
#include <signal.h> void psignal(int四個,const char *msg); |
另一個常用函式是strsignal,返回signo對應的描述
#include <string.h> char *strsignal(int signo); 返回對應的描述 |
Solaris提供把signal和名稱對應起來的函式:
#include <string.h> int *sig2str(int signo, char *str); int str2sig(const char *str, int *signop); 返回對應的描述 |
Sig2str把signal轉換為對應的描述字串,而str2sig則是將描述字串轉換為signal。
相關文章
- 程式程式設計3 - UNIX高階環境程式設計第9章讀書筆記程式設計筆記
- 程式程式設計1 – Unix環境高階程式設計7章讀書筆記程式設計筆記
- 程式程式設計2 – Unix環境高階程式設計8章讀書筆記程式設計筆記
- 使用pthread庫進行多執行緒程式設計1 - UNIX環境高階程式設計第11章讀書筆記thread執行緒程式設計筆記
- 使用pthread庫進行多執行緒程式設計2 - UNIX高階環境程式設計第12章讀書筆記thread執行緒程式設計筆記
- UNIX系統程式設計的瑞士軍刀 --《UNIX環境高階程式設計》書評程式設計
- unix環境高階程式設計(中)-程式篇程式設計
- UNIX環境高階程式設計習題——第二章程式設計
- Unix環境高階程式設計——第一章-UNIX基礎知識程式設計
- unix環境高階程式設計(下)-高階IO和程式間通訊篇程式設計
- unix環境高階程式設計(上)-檔案篇程式設計
- UNIX環境高階程式設計——執行緒和fork程式設計執行緒
- 《UNIX環境高階程式設計》(APUE) 筆記第十一章 - 執行緒程式設計筆記執行緒
- 【讀書筆記】JavaScript高階程式設計(第3版)(第5-7章)筆記JavaScript程式設計
- Unix環境程式設計之定時、訊號與中斷程式設計
- Java高階程式設計筆記 • 【第4章 網路程式設計】Java程式設計筆記
- 推薦一本技術類書籍:Unix環境高階程式設計程式設計
- 《unix環境高階程式設計》書中原始碼編譯問題解決程式設計原始碼編譯
- Unix環境高階程式設計——開源的標準化努力程式設計
- C#高階程式設計 讀書筆記C#程式設計筆記
- 《LINUX與UNIX SHELL程式設計指南》讀書筆記(轉)Linux程式設計筆記
- 《UNIX環境高階程式設計》apue原始碼Ubuntu下的編譯程式設計原始碼Ubuntu編譯
- 第一篇:《UNIX 環境高階程式設計》編譯環境的搭建程式設計編譯
- Unix高階程式設計學習筆記--系統呼叫簡介程式設計筆記
- 《APUE》第7章 程式環境-讀書筆記筆記
- JavaScript 高階程式設計 第三章 讀書筆記(1)JavaScript程式設計筆記
- python高階程式設計讀書筆記(一)Python程式設計筆記
- 「理解Unix程式」讀書筆記筆記
- Objective-C高階程式設計讀書筆記(二)Object程式設計筆記
- Objective-C高階程式設計讀書筆記(一)Object程式設計筆記
- 《JavaScript 高階程式設計》精讀筆記JavaScript程式設計筆記
- UNIX環境程式設計-第四章檔案和目錄程式設計
- Objective-C高階程式設計讀書筆記之GCDObject程式設計筆記GC
- Objective-C高階程式設計讀書筆記之blocksObject程式設計筆記BloC
- 第31章:高階型別程式設計型別程式設計
- 【筆記】《JavaScript高階程式設計(第3版)》(1)筆記JavaScript程式設計
- 【筆記】《JavaScript高階程式設計(第3版)》(2)筆記JavaScript程式設計
- JavaScript高階程式設計筆記JavaScript程式設計筆記