信號(hào)本質(zhì)
信號(hào)是在軟件層次上對(duì)中斷機(jī)制的一種模擬,在原理上,一個(gè)進(jìn)程收到一個(gè)信號(hào)與處理器收到一個(gè)中斷請(qǐng)求可以說(shuō)是一樣的。信號(hào)是異步的,一個(gè)進(jìn)程不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá),事實(shí)上,進(jìn)程也不知道信號(hào)到底什么時(shí)候到達(dá)。
信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號(hào)的進(jìn)程有哪些事情發(fā)生了。信號(hào)機(jī)制經(jīng)過(guò)POSIX實(shí)時(shí)擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。
信號(hào)來(lái)源
信號(hào)事件的發(fā)生有兩個(gè)來(lái)源:硬件來(lái)源(比如我們按下了鍵盤(pán)或者其它硬件故障);軟件來(lái)源,最常用發(fā)送信號(hào)的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer以及sigqueue函數(shù),軟件來(lái)源還包括一些非法運(yùn)算等操作。
信號(hào)的種類(lèi)系統(tǒng)支持的所有信號(hào)如下所示:
進(jìn)程可以屏蔽掉大多數(shù)信號(hào),除了SIGSTOP和SIGKILL。
進(jìn)程對(duì)信號(hào)的響應(yīng)進(jìn)程可以通過(guò)三種方式來(lái)響應(yīng)一個(gè)信號(hào):(1)忽略信號(hào),即對(duì)信號(hào)不做任何處理,其中,有兩個(gè)信號(hào)不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號(hào)。定義信號(hào)處理函數(shù),當(dāng)信號(hào)發(fā)生時(shí),執(zhí)行相應(yīng)的處理函數(shù);(3)執(zhí)行缺省操作,linux對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作。注意,進(jìn)程對(duì)實(shí)時(shí)信號(hào)的缺省反應(yīng)是進(jìn)程終止。
Linux究竟采用上述三種方式的哪一個(gè)來(lái)響應(yīng)信號(hào),取決于傳遞給相應(yīng)API函數(shù)的參數(shù)。
信號(hào)的安裝如果進(jìn)程要處理某一信號(hào),那么就要在進(jìn)程中安裝該信號(hào)。安裝信號(hào)主要用來(lái)確定信號(hào)值及進(jìn)程針對(duì)該信號(hào)值的動(dòng)作之間的映射關(guān)系,即進(jìn)程將要處理哪個(gè)信號(hào);該信號(hào)被傳遞給進(jìn)程時(shí),將執(zhí)行何種操作。
Linux主要有兩個(gè)函數(shù)實(shí)現(xiàn)信號(hào)的安裝:signal()、sigaction()。其中signal()在可靠信號(hào)系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn), 是庫(kù)函數(shù)。它只有兩個(gè)參數(shù),不支持信號(hào)傳遞信息,主要是用于前32種非實(shí)時(shí)信號(hào)的安裝;而sigaction()是較新的函數(shù)(由兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn):sys_signal以及sys_rt_sigaction),有三個(gè)參數(shù),支持信號(hào)傳遞信息,主要用來(lái)與 sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時(shí)信號(hào)的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)在支持信號(hào)帶有參數(shù)。
signaltypedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler));
第一個(gè)參數(shù)指定信號(hào)的值,第二個(gè)參數(shù)指定針對(duì)前面信號(hào)值的處理,可以忽略該信號(hào)(參數(shù)設(shè)為SIG_IGN);可以采用系統(tǒng)默認(rèn)方式處理信號(hào)(參數(shù)設(shè)為SIG_DFL);也可以自己實(shí)現(xiàn)處理方式(參數(shù)指定一個(gè)函數(shù)地址)。
如果signal()調(diào)用成功,返回最后一次為安裝信號(hào)signum而調(diào)用signal()時(shí)的handler值;失敗則返回SIG_ERR。
sigactionint sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
sigaction函數(shù)用于改變進(jìn)程接收到特定信號(hào)后的行為。該函數(shù)的第一個(gè)參數(shù)為信號(hào)的值,可以為除SIGKILL及SIGSTOP外的任何一個(gè)特定有效的信號(hào)(為這兩個(gè)信號(hào)定義自己的處理函數(shù),將導(dǎo)致信號(hào)安裝錯(cuò)誤)。第二個(gè)參數(shù)是指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例的指針,在結(jié)構(gòu) sigaction的實(shí)例中,指定了對(duì)特定信號(hào)的處理,可以為空,進(jìn)程會(huì)以缺省方式對(duì)信號(hào)處理;第三個(gè)參數(shù)oldact指向的對(duì)象用來(lái)保存原來(lái)對(duì)相應(yīng)信號(hào)的處理,可指定oldact為NULL。如果把第二、第三個(gè)參數(shù)都設(shè)為NULL,那么該函數(shù)可用于檢查信號(hào)的有效性。
第二個(gè)參數(shù)最為重要,其中包含了對(duì)指定信號(hào)的處理、信號(hào)所傳遞的信息、信號(hào)處理函數(shù)執(zhí)行過(guò)程中應(yīng)屏蔽掉哪些函數(shù)等等。
信號(hào)的發(fā)送發(fā)送信號(hào)的主要函數(shù)有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。
killint kill(pid_t pid, int signo);
signo是信號(hào)值,當(dāng)為0時(shí)(即空信號(hào)),實(shí)際不發(fā)送任何信號(hào),但照常進(jìn)行錯(cuò)誤檢查,因此,可用于檢查目標(biāo)進(jìn)程是否存在,以及當(dāng)前進(jìn)程是否具有向目標(biāo)發(fā)送信號(hào)的權(quán)限(root權(quán)限的進(jìn)程可以向任何進(jìn)程發(fā)送信號(hào),非root權(quán)限的進(jìn)程只能向?qū)儆谕粋€(gè)session或者同一個(gè)用戶(hù)的進(jìn)程發(fā)送信號(hào))。
raiseint raise(int signo);
向進(jìn)程本身發(fā)送信號(hào),參數(shù)為即將發(fā)送的信號(hào)值。調(diào)用成功返回 0;否則,返回 -1。
sigqueueint sigqueue(pid_t pid, int signo, const union sigval val);
sigqueue()與函數(shù)sigaction()配合使用,sigqueue的第一個(gè)參數(shù)是指定接收信號(hào)的進(jìn)程ID,第二個(gè)參數(shù)確定即將發(fā)送的信號(hào),第三個(gè)參數(shù)是一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu)union sigval,指定了信號(hào)傳遞的參數(shù),即通常所說(shuō)的4字節(jié)值。
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個(gè)進(jìn)程發(fā)送信號(hào),而不能發(fā)送信號(hào)給一個(gè)進(jìn)程組。如果signo=0,將會(huì)執(zhí)行錯(cuò)誤檢查,但實(shí)際上不發(fā)送任何信號(hào),0值信號(hào)可用于檢查pid的有效性以及當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號(hào)。在調(diào)用sigqueue時(shí),sigval_t指定的信息會(huì)拷貝到3參數(shù)信號(hào)處理函數(shù)(3參數(shù)信號(hào)處理函數(shù)指的是信號(hào)處理函數(shù)由 sigaction安裝,并設(shè)定了sa_sigaction指針)的siginfo_t結(jié)構(gòu)中,這樣信號(hào)處理函數(shù)就可以處理這些信息了。由于 sigqueue系統(tǒng)調(diào)用支持發(fā)送帶參數(shù)信號(hào),所以比kill()系統(tǒng)調(diào)用的功能要靈活和強(qiáng)大得多。
注:sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),第三個(gè)參數(shù)包含的信息仍然能夠傳遞給信號(hào)處理函數(shù); sigqueue()發(fā)送非實(shí)時(shí)信號(hào)時(shí),仍然不支持排隊(duì),即在信號(hào)處理函數(shù)執(zhí)行過(guò)程中到來(lái)的所有相同信號(hào),都被合并為一個(gè)信號(hào)。
alarmunsigned int alarm(unsigned int seconds);
專(zhuān)門(mén)為SIGALRM信號(hào)而設(shè),在指定的時(shí)間seconds秒后,將向進(jìn)程本身發(fā)送SIGALRM信號(hào),又稱(chēng)為鬧鐘時(shí)間。進(jìn)程調(diào)用alarm后,任何以前的alarm()調(diào)用都將無(wú)效。如果參數(shù)seconds為零,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時(shí)間。返回值,如果調(diào)用alarm()前,進(jìn)程中已經(jīng)設(shè)置了鬧鐘時(shí)間,則返回上一個(gè)鬧鐘時(shí)間的剩余時(shí)間,否則返回0。
settimerint setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能強(qiáng)大,支持3種類(lèi)型的定時(shí)器:
setitimer()第一個(gè)參數(shù)which指定定時(shí)器類(lèi)型(上面三種之一);第二個(gè)參數(shù)是結(jié)構(gòu)itimerval的一個(gè)實(shí)例;第三個(gè)參數(shù)可不做處理。setitimer()調(diào)用成功返回0,否則返回-1。
abortvoid abort(void);
向進(jìn)程發(fā)送SIGABORT信號(hào),默認(rèn)情況下進(jìn)程會(huì)異常退出,當(dāng)然可定義自己的信號(hào)處理函數(shù)。即使SIGABORT被進(jìn)程設(shè)置為阻塞信號(hào),調(diào)用abort()后,SIGABORT仍然能被進(jìn)程接收。該函數(shù)無(wú)返回值。
信號(hào)生命周期對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來(lái)說(shuō),可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來(lái)刻畫(huà):信號(hào)誕生;信號(hào)在進(jìn)程中注冊(cè)完畢;信號(hào)在進(jìn)程中的注銷(xiāo)完畢;信號(hào)處理函數(shù)執(zhí)行完畢。
新聞熱點(diǎn)
疑難解答
圖片精選