青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

isware

linux多線程及信號處理

linux信號種類

1、可靠信號和不可靠信號

      "不可靠信號"
  
    Linux信號機(jī)制基本上是從Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機(jī)制比較簡單和原始,后來在實(shí)踐中暴露出一些問題,因此,把那些建立在 早期機(jī)制上的信號叫做"不可靠信號",信號值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。他的主要問題是:
  
    • 進(jìn)程每次處理信號后,就將對信號的響應(yīng)配置為默認(rèn)動(dòng)作。在某些情況下,將導(dǎo)致對信號的錯(cuò)誤處理;因此,用戶假如不希望這樣的操作,那么就要在信號處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號。
  
   • 信號可能丟失,后面將對此周詳闡述。
  
   因此,早期unix下的不可靠信號主要指的是進(jìn)程可能對信號做出錯(cuò)誤的反應(yīng)連同信號可能丟失。
  
    Linux支持不可靠信號,但是對不可靠信號機(jī)制做了改進(jìn):在調(diào)用完信號處理函數(shù)后,不必重新調(diào)用該信號的安裝函數(shù)(信號安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此,Linux下的不可靠信號問題主要指的是信號可能丟失。
  
    "可靠信號"
      隨著時(shí)間的發(fā)展,實(shí)踐證實(shí)了有必要對信號的原始機(jī)制加以改進(jìn)和擴(kuò)充。所以,后來出現(xiàn)的各種Unix版本分別在這方面進(jìn)行了研究,力圖實(shí)現(xiàn)"可靠信 號"。由于原來定義的信號已有許多應(yīng)用,不好再做改變,最終只好又新增加了一些信號,并在一開始就把他們定義為可靠信號,這些信號支持排隊(duì),不會丟失。同 時(shí),信號的發(fā)送和安裝也出現(xiàn)了新版本:信號發(fā)送函數(shù)sigqueue()及信號安裝函數(shù)sigaction()。POSIX.4對可靠信號機(jī)制做了標(biāo)準(zhǔn) 化。但是,POSIX只對可靠信號機(jī)制應(yīng)具備的功能連同信號機(jī)制的對外接口做了標(biāo)準(zhǔn)化,對信號機(jī)制的實(shí)現(xiàn)沒有作具體的規(guī)定。
      信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝 函數(shù)sigation()連同信號發(fā)送函數(shù)sigqueue()的同時(shí),仍然支持早期的signal()信號安裝函數(shù),支持信號發(fā)送函數(shù)kill()。
      注:不要有這樣的誤解:由sigqueue()發(fā)送、sigaction安裝的信號就是可靠的。事實(shí)上,可靠信號是指后來添加的新信號(信號值 位于SIGRTMIN及SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠和不可靠只和信號值有關(guān),和信號的發(fā)送及安裝 函數(shù)無關(guān)。現(xiàn)在linux中的signal()是通過sigation()函數(shù)實(shí)現(xiàn)的,因此,即使通過signal()安裝的信號,在信號處理函數(shù)的結(jié)尾 也不必再調(diào)用一次信號安裝函數(shù)。同時(shí),由signal()安裝的實(shí)時(shí)信號支持排隊(duì),同樣不會丟失。
      對于現(xiàn)在linux的兩個(gè)信號安裝函數(shù):signal()及sigaction()來說,他們都不能把SIGRTMIN以前的信號變成可靠信號 (都不支持排隊(duì),仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊(duì)。這兩個(gè)函數(shù)的最大區(qū)別在于,經(jīng)過sigaction安 裝的信號都能傳遞信息給信號處理函數(shù)(對任何信號這一點(diǎn)都成立),而經(jīng)過signal安裝的信號卻不能向信號處理函數(shù)傳遞信息。對于信號發(fā)送函數(shù)來說也是 相同的。

2、實(shí)時(shí)信號和非實(shí)時(shí)信號

      早期Unix系統(tǒng)只定義了32種信號,Ret hat7.2支持64種信號,編號0-63(SIGRTMIN=31,SIGRTMAX=63),將來可能進(jìn)一步增加,這需要得到內(nèi)核的支持。前32種信 號已有了預(yù)定義值,每個(gè)信號有了確定的用途及含義,并且每種信號都有各自的缺省動(dòng)作。如按鍵盤的CTRL ^C時(shí),會產(chǎn)生SIGINT信號,對該信號的默認(rèn)反應(yīng)就是進(jìn)程終止。后32個(gè)信號表示實(shí)時(shí)信號,等同于前面闡述的可靠信號。這確保了發(fā)送的多個(gè)實(shí)時(shí)信號都 被接收。實(shí)時(shí)信號是POSIX標(biāo)準(zhǔn)的一部分,可用于應(yīng)用進(jìn)程。
      非實(shí)時(shí)信號都不支持排隊(duì),都是不可靠信號;實(shí)時(shí)信號都支持排隊(duì),都是可靠信號。

Linux的信號的種類有60多種。可以用kill -l命令查看所有的信號,每個(gè)信號的含義如下:

1) SIGHUP:當(dāng)用戶退出shell時(shí),由該shell啟動(dòng)的所有進(jìn)程將收到這個(gè)信號,默認(rèn)動(dòng)作為終止進(jìn)程

2)SIGINT:當(dāng)用戶按下了<Ctrl+C>組合鍵時(shí),用戶終端向正在運(yùn)行中的由該終端啟動(dòng)的程序發(fā)出此信號。默認(rèn)動(dòng)作為終止里程。

3)SIGQUIT:當(dāng)用戶按下<ctrl+\>組合鍵時(shí)產(chǎn)生該信號,用戶終端向正在運(yùn)行中的由該終端啟動(dòng)的程序發(fā)出些信號。默認(rèn)動(dòng)作為終止進(jìn)程。

4)SIGILL:CPU檢測到某進(jìn)程執(zhí)行了非法指令。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件

5)SIGTRAP:該信號由斷點(diǎn)指令或其他 trap指令產(chǎn)生。默認(rèn)動(dòng)作為終止里程 并產(chǎn)生core文件。

6 ) SIGABRT:調(diào)用abort函數(shù)時(shí)產(chǎn)生該信號。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。

7)SIGBUS:非法訪問內(nèi)存地址,包括內(nèi)存對齊出錯(cuò),默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。

8)SIGFPE:在發(fā)生致命的運(yùn)算錯(cuò)誤時(shí)發(fā)出。不僅包括浮點(diǎn)運(yùn)算錯(cuò)誤,還包括溢出及除數(shù)為0等所有的算法錯(cuò)誤。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。

9)SIGKILL:無條件終止進(jìn)程。本信號不能被忽略,處理和阻塞。默認(rèn)動(dòng)作為終止進(jìn)程。它向系統(tǒng)管理員提供了可以殺死任何進(jìn)程的方法。

10)SIGUSE1:用戶定義 的信號。即程序員可以在程序中定義并使用該信號。默認(rèn)動(dòng)作為終止進(jìn)程。

11)SIGSEGV:指示進(jìn)程進(jìn)行了無數(shù)內(nèi)存訪問。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。

12)SIGUSR2:這是另外一個(gè)用戶自定義信號 ,程序員可以在程序中定義 并使用該信號。默認(rèn)動(dòng)作為終止進(jìn)程。1

13)SIGPIPE:Broken pipe向一個(gè)沒有讀端的管道寫數(shù)據(jù)。默認(rèn)動(dòng)作為終止進(jìn)程。

14 ) SIGALRM:定時(shí)器超時(shí),超時(shí)的時(shí)間 由系統(tǒng)調(diào)用alarm設(shè)置。默認(rèn)動(dòng)作為終止進(jìn)程。

15)SIGTERM:程序結(jié)束信號,與SIGKILL不同的是,該信號可以被阻塞和終止。通常用來要示程序正常退出。執(zhí)行shell命令Kill時(shí),缺省產(chǎn)生這個(gè)信號。默認(rèn)動(dòng)作為終止進(jìn)程。

16)SIGCHLD:子進(jìn)程結(jié)束時(shí),父進(jìn)程會收到這個(gè)信號。默認(rèn)動(dòng)作為忽略這個(gè)信號。

17)SIGCONT:停止進(jìn)程的執(zhí)行。信號不能被忽略,處理和阻塞。默認(rèn)動(dòng)作為終止進(jìn)程。

18)SIGTTIN:停止進(jìn)程的運(yùn)行,但該信號可以被處理和忽略。按下<ctrl+z>組合鍵發(fā)出災(zāi)個(gè)信號。默認(rèn)動(dòng)作為暫停進(jìn)程。

19)SIGTSTP:停止進(jìn)程的運(yùn)行,可該信號可以被處理可忽略。按下<ctrl+z>組合鍵時(shí)發(fā)出這個(gè)信號。默認(rèn)動(dòng)作為暫停進(jìn)程。

21)SIGTTOU:該信號類似于SIGTTIN,在后臺進(jìn)程要向終端輸出數(shù)據(jù)時(shí)發(fā)生。默認(rèn)動(dòng)作為暫停進(jìn)程。

22)SIGURG:套接字上有緊急數(shù)據(jù)時(shí),向當(dāng)前正在運(yùn)行的進(jìn)程發(fā)出些信號,報(bào)告有緊急數(shù)據(jù)到達(dá)。默認(rèn)動(dòng)作為忽略該信號。

23)SIGXFSZ:進(jìn)程執(zhí)行時(shí)間超過了分配給該進(jìn)程的CPU時(shí)間 ,系統(tǒng)產(chǎn)生該信號并發(fā)送給該進(jìn)程。默認(rèn)動(dòng)作為終止進(jìn)程。

24)SIGXFSZ:超過文件的最大長度設(shè)置。默認(rèn)動(dòng)作為終止進(jìn)程。

25)SIGVTALRM:虛擬時(shí)鐘超時(shí)時(shí)產(chǎn)生該信號。類似于SIGALRM,但是該信號只計(jì)算該進(jìn)程占用CPU的使用時(shí)間。默認(rèn)動(dòng)作為終止進(jìn)程。

26)SGIPROF:類似于SIGVTALRM,它不公包括該進(jìn)程占用CPU時(shí)間還包括執(zhí)行系統(tǒng)調(diào)用時(shí)間。默認(rèn)動(dòng)作為終止進(jìn)程。

27)SIGWINCH:窗口變化大小時(shí)發(fā)出。默認(rèn)動(dòng)作為忽略該信號。

28)SIGIO:此信號向進(jìn)程指示發(fā)出了一個(gè)異步IO事件。默認(rèn)動(dòng)作為忽略。

29)SIGPWR:關(guān)機(jī)。默認(rèn)動(dòng)作為終止進(jìn)程。

30)SIGSYS:無效的系統(tǒng)調(diào)用。默認(rèn)動(dòng)作為終止進(jìn)程并產(chǎn)生core文件。

31)SIGRTMIN~(64)SIGRTMAX:LINUX的實(shí)時(shí)信號,它們沒有固定的含義(可以由用戶自定義)。所有的實(shí)時(shí)信號的默認(rèn)動(dòng)作都為終止進(jìn)程。


進(jìn)程對信號的響應(yīng)

 

   進(jìn)程能夠通過三種方式來響應(yīng)一個(gè)信號:(1)忽略信號,即對信號不做任何處理,其中,有兩個(gè)信號不能忽略:SIGKILL及SIGSTOP; (2)捕獲信號。定義信號處理函數(shù),當(dāng)信號發(fā)生時(shí),執(zhí)行相應(yīng)的處理函數(shù);(3)執(zhí)行缺省操作,Linux對每種信號都規(guī)定了默認(rèn)操作,周詳情況請參考 [2]連同其他資料。注意,進(jìn)程對實(shí)時(shí)信號的缺省反應(yīng)是進(jìn)程終止。
   Linux究竟采用上述三種方式的哪一個(gè)來響應(yīng)信號,取決于傳遞給相應(yīng)API函數(shù)的參數(shù)。

信號的發(fā)送


發(fā)送信號的主要函數(shù)有:kill()、raise()、 sigqueue()、alarm()、setitimer()連同abort()。
1、kill()
#include
#include
int kill(pid_t pid,int signo)
參數(shù)pid的值 信號的接收進(jìn)程
pid>0 進(jìn)程ID為pid的進(jìn)程
pid=0 同一個(gè)進(jìn)程組的進(jìn)程
pid<0 pid!=-1 進(jìn)程組ID為 -pid的任何進(jìn)程
pid=-1 除發(fā)送進(jìn)程自身外,任何進(jìn)程ID大于1的進(jìn)程
Sinno是信號值,當(dāng)為0時(shí)(即空信號),實(shí)際不發(fā)送任何信號,但照常進(jìn)行錯(cuò)誤檢查,因此,可用于檢查目標(biāo)進(jìn)程是否存在,連同當(dāng)前進(jìn)程是否具備 向目標(biāo)發(fā)送信號的權(quán)限(root權(quán)限的進(jìn)程能夠向任何進(jìn)程發(fā)送信號,非root權(quán)限的進(jìn)程只能向?qū)儆谕粋€(gè)session或同一個(gè)用戶的進(jìn)程發(fā)送信 號)。
Kill()最常用于pid>0時(shí)的信號發(fā)送,調(diào)用成功返回 0; 否則,返回 -1。注:對于pid<0時(shí)的情況,對于哪些進(jìn)程將接受信號,各種版本說法不一,其實(shí)很簡單,參閱內(nèi)核源碼kernal/signal.c即可,上 表中的規(guī)則是參考red hat 7.2。
2、raise()
#include
int raise(int signo)
3、sigqueue()
#include
#include
int sigqueue(pid_t pid, int sig, const union sigval val)
調(diào)用成功返回 0;否則,返回 -1。
sigqueue()是比較新的發(fā)送信號系統(tǒng)調(diào)用,主要是針對實(shí)時(shí)信號提出的(當(dāng)然也支持前32種),支持信號帶有參數(shù),和函數(shù)sigaction()配合使用。
sigqueue的第一個(gè)參數(shù)是指定接收信號的進(jìn)程ID,第二個(gè)參數(shù)確定即將發(fā)送的信號,第三個(gè)參數(shù)是個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu)union sigval,指定了信號傳遞的參數(shù),即通常所說的4字節(jié)值。
typedef union sigval {
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個(gè)進(jìn)程發(fā)送信號,而不能發(fā)送信號給一個(gè)進(jìn)程組。假如 signo=0,將會執(zhí)行錯(cuò)誤檢查,但實(shí)際上不發(fā)送任何信號,0值信號可用于檢查pid的有效性連同當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號。
在調(diào)用sigqueue時(shí),sigval_t指定的信息會拷貝到3參數(shù)信號處理函數(shù)(3參數(shù)信號處理函數(shù)指的是信號處理函數(shù)由 sigaction安裝,并設(shè)定了sa_sigaction指針,稍后將闡述)的siginfo_t結(jié)構(gòu)中,這樣信號處理函數(shù)就能夠處理這些信息了。由于 sigqueue系統(tǒng)調(diào)用支持發(fā)送帶參數(shù)信號,所以比kill()系統(tǒng)調(diào)用的功能要靈活和強(qiáng)大得多。
注:sigqueue()發(fā)送非實(shí)時(shí)信號時(shí),第三個(gè)參數(shù)包含的信息仍然能夠傳遞給信號處理函數(shù); sigqueue()發(fā)送非實(shí)時(shí)信號時(shí),仍然不支持排隊(duì),即在信號處理函數(shù)執(zhí)行過程中到來的任何相同信號,都被合并為一個(gè)信號。
4、alarm()
#include
unsigned int alarm(unsigned int seconds)
專門為SIGALRM信號而設(shè),在指定的時(shí)間seconds秒后,將向進(jìn)程本身發(fā)送SIGALRM信號,又稱為鬧鐘時(shí)間。進(jìn)程調(diào)用alarm后,任何以前的alarm()調(diào)用都將無效。假如參數(shù)seconds為零,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時(shí)間。
5、setitimer()
#include
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能強(qiáng)大,支持3種類型的定時(shí)器:
• ITIMER_REAL: 設(shè)定絕對時(shí)間;經(jīng)過指定的時(shí)間后,內(nèi)核將發(fā)送SIGALRM信號給本進(jìn)程;
• ITIMER_VIRTUAL 設(shè)定程式執(zhí)行時(shí)間;經(jīng)過指定的時(shí)間后,內(nèi)核將發(fā)送SIGVTALRM信號給本進(jìn)程;
• ITIMER_PROF 設(shè)定進(jìn)程執(zhí)行連同內(nèi)核因本進(jìn)程而消耗的時(shí)間和,經(jīng)過指定的時(shí)間后,內(nèi)核將發(fā)送ITIMER_VIRTUAL信號給本進(jìn)程;
Setitimer()第一個(gè)參數(shù)which指定定時(shí)器類型(上面三種之一);第二個(gè)參數(shù)是結(jié)構(gòu)itimerval的一個(gè)實(shí)例,結(jié)構(gòu)itimerval形式見附錄1。第三個(gè)參數(shù)可不做處理。
Setitimer()調(diào)用成功返回0,否則返回-1。
6、abort()
#include
void abort(void);
向進(jìn)程發(fā)送SIGABORT信號,默認(rèn)情況下進(jìn)程會異常退出,當(dāng)然可定義自己的信號處理函數(shù)。即使SIGABORT被進(jìn)程配置為阻塞信號,調(diào)用abort()后,SIGABORT仍然能被進(jìn)程接收。該函數(shù)無返回值。

信號安裝

假如進(jìn)程要處理某一信號,那么就要在進(jìn)程中安裝該信號。安裝信號主要用來確定信號值及進(jìn)程針對該信號值的動(dòng)作之間的映射關(guān)系,即進(jìn)程將要處理哪個(gè)信號;該信號被傳遞給進(jìn)程時(shí),將執(zhí)行何種操作。
linux主要有兩個(gè)函數(shù)實(shí)現(xiàn)信號的安裝:signal()、sigaction()。其中signal()在可靠信號系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn), 是庫函數(shù)。他只有兩個(gè)參數(shù),不支持信號傳遞信息,主要是用于前32種非實(shí)時(shí)信號的安裝;而sigaction()是較新的函數(shù)(由兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn): sys_signal連同sys_rt_sigaction),有三個(gè)參數(shù),支持信號傳遞信息,主要用來和 sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時(shí)信號的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)在支持信號帶有參數(shù)。
1、signal()
#include
void (*signal(int signum, void (*handler))(int)))(int);
假如該函數(shù)原型不容易理解的話,能夠參考下面的分解方式來理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一個(gè)參數(shù)指定信號的值,第二個(gè)參數(shù)指定針對前面信號值的處理,能夠忽略該信號(參數(shù)設(shè)為SIG_IGN);能夠采用系統(tǒng)默認(rèn)方式處理信號(參數(shù)設(shè)為SIG_DFL);也能夠自己實(shí)現(xiàn)處理方式(參數(shù)指定一個(gè)函數(shù)地址)。
假如signal()調(diào)用成功,返回最后一次為安裝信號signum而調(diào)用signal()時(shí)的handler值;失敗則返回SIG_ERR。
2、sigaction()
#include
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
sigaction函數(shù)用于改變進(jìn)程接收到特定信號后的行為。該函數(shù)的第一個(gè)參數(shù)為信號的值,能夠?yàn)槌齋IGKILL及SIGSTOP外的任何一 個(gè)特定有效的信號(為這兩個(gè)信號定義自己的處理函數(shù),將導(dǎo)致信號安裝錯(cuò)誤)。第二個(gè)參數(shù)是指向結(jié)構(gòu)sigaction的一個(gè)實(shí)例的指針,在結(jié)構(gòu) sigaction的實(shí)例中,指定了對特定信號的處理,能夠?yàn)榭眨M(jìn)程會以缺省方式對信號處理;第三個(gè)參數(shù)oldact指向的對象用來保存原來對相應(yīng)信號 的處理,可指定oldact為NULL。假如把第二、第三個(gè)參數(shù)都設(shè)為NULL,那么該函數(shù)可用于檢查信號的有效性。
第二個(gè)參數(shù)最為重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數(shù)執(zhí)行過程中應(yīng)屏蔽掉哪些函數(shù)等等。
sigaction結(jié)構(gòu)定義如下:

struct sigaction {
union{
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int,struct siginfo *, void *);
}_u
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
}

其中,sa_restorer,已過時(shí),POSIX不支持他,不應(yīng)再被使用。
1、聯(lián)合數(shù)據(jù)結(jié)構(gòu)中的兩個(gè)元素_sa_handler連同*_sa_sigaction指定信號關(guān)聯(lián)函數(shù),即用戶指定的信號處理函數(shù)。除了能夠是用戶自定義的處理函數(shù)外,還能夠?yàn)镾IG_DFL(采用缺省的處理方式),也能夠?yàn)镾IG_IGN(忽略信號)。
2、由_sa_handler指定的處理函數(shù)只有一個(gè)參數(shù),即信號值,所以信號不能傳遞除信號值之外的任何信息;由_sa_sigaction是 指定的信號處理函數(shù)帶有三個(gè)參數(shù),是為實(shí)時(shí)信號而設(shè)的(當(dāng)然同樣支持非實(shí)時(shí)信號),他指定一個(gè)3參數(shù)信號處理函數(shù)。第一個(gè)參數(shù)為信號值,第三個(gè)參數(shù)沒有使 用(posix沒有規(guī)范使用該參數(shù)的標(biāo)準(zhǔn)),第二個(gè)參數(shù)是指向siginfo_t結(jié)構(gòu)的指針,結(jié)構(gòu)中包含信號攜帶的數(shù)據(jù)值,參數(shù)所指向的結(jié)構(gòu)如下:

siginfo_t {
int si_signo; /* 信號值,對任何信號有意義*/
int si_errno; /* errno值,對任何信號有意義*/
int si_code; /* 信號產(chǎn)生的原因,對任何信號有意義*/
union{ /* 聯(lián)合數(shù)據(jù)結(jié)構(gòu),不同成員適應(yīng)不同信號 */
//確保分配足夠大的存儲空間
int _pad[SI_PAD_SIZE];
//對SIGKILL有意義的結(jié)構(gòu)
struct{
...
}...

... ...
... ...
//對SIGILL, SIGFPE, SIGSEGV, SIGBUS有意義的結(jié)構(gòu)
struct{
...
}...
... ...
}
}

注:為了更便于閱讀,在說明問題時(shí)常把該結(jié)構(gòu)表示為附錄2所表示的形式。
siginfo_t結(jié)構(gòu)中的聯(lián)合數(shù)據(jù)成員確保該結(jié)構(gòu)適應(yīng)任何的信號,比如對于實(shí)時(shí)信號來說,則實(shí)際采用下面的結(jié)構(gòu)形式:

typedef struct {
int si_signo;
int si_errno;
int si_code;
union sigval si_value;
} siginfo_t;

結(jié)構(gòu)的第四個(gè)域同樣為一個(gè)聯(lián)合數(shù)據(jù)結(jié)構(gòu):

union sigval {
int sival_int;
void *sival_ptr;
}
采用聯(lián)合數(shù)據(jù)結(jié)構(gòu),說明siginfo_t結(jié)構(gòu)中的si_value要么持有一個(gè)4字節(jié)的整數(shù)值,要么持有一個(gè)指針,這就構(gòu)成了和信號相關(guān)的數(shù) 據(jù)。在信號的處理函數(shù)中,包含這樣的信號相關(guān)數(shù)據(jù)指針,但沒有規(guī)定具體如何對這些數(shù)據(jù)進(jìn)行操作,操作方法應(yīng)該由程式研發(fā)人員根據(jù)具體任務(wù)事先約定。
前面在討論系統(tǒng)調(diào)用sigqueue發(fā)送信號時(shí),sigqueue的第三個(gè)參數(shù)就是sigval聯(lián)合數(shù)據(jù)結(jié)構(gòu),當(dāng)調(diào)用sigqueue時(shí),該數(shù) 據(jù)結(jié)構(gòu)中的數(shù)據(jù)就將拷貝到信號處理函數(shù)的第二個(gè)參數(shù)中。這樣,在發(fā)送信號同時(shí),就能夠讓信號傳遞一些附加信息。信號能夠傳遞信息對程式研發(fā)是很有意義 的。
信號參數(shù)的傳遞過程可圖示如下:

3、sa_mask指定在信號處理程式執(zhí)行過程中,哪些信號應(yīng)當(dāng)被阻塞。缺省情況下當(dāng)前信號本身被阻塞,防止信號的嵌套發(fā)送,除非指定SA_NODEFER或SA_NOMASK標(biāo)志位。
注:請注意sa_mask指定的信號阻塞的前提條件,是在由sigaction()安裝信號的處理函數(shù)執(zhí)行過程中由sa_mask指定的信號才被阻塞。
4、sa_flags中包含了許多標(biāo)志位,包括剛剛提到的SA_NODEFER及SA_NOMASK標(biāo)志位。另一個(gè)比較重要的標(biāo)志位是 SA_SIGINFO,當(dāng)設(shè)定了該標(biāo)志位時(shí),表示信號附帶的參數(shù)能夠被傳遞到信號處理函數(shù)中,因此,應(yīng)該為sigaction結(jié)構(gòu)中的 sa_sigaction指定處理函數(shù),而不應(yīng)該為sa_handler指定信號處理函數(shù),否則,配置該標(biāo)志變得毫無意義。即使為 sa_sigaction指定了信號處理函數(shù),假如不配置SA_SIGINFO,信號處理函數(shù)同樣不能得到信號傳遞過來的數(shù)據(jù),在信號處理函數(shù)中對這些信 息的訪問都將導(dǎo)致段錯(cuò)誤(Segmentation fault)。
注:很多文獻(xiàn)在闡述該標(biāo)志位時(shí)都認(rèn)為,假如配置了該標(biāo)志位,就必須定義三參數(shù)信號處理函數(shù)。實(shí)際不是這樣的,驗(yàn)證方法很簡單:自己實(shí)現(xiàn)一個(gè)單一 參數(shù)信號處理函數(shù),并在程式中配置該標(biāo)志位,能夠察看程式的運(yùn)行結(jié)果。實(shí)際上,能夠把該標(biāo)志位看成信號是否傳遞參數(shù)的開關(guān),假如配置該位,則傳遞參數(shù);否 則,不傳遞參數(shù)。

Linux信號阻塞和信號未決

1. 信號掩碼——被阻塞的信號集
  每個(gè)進(jìn)程都有一個(gè)用來描述哪些信號傳送來將被阻塞的信號集,如果某種信號在某個(gè)進(jìn)程的阻塞信號集中,則傳送到該進(jìn)程的此種信號將會被阻塞。當(dāng)前被進(jìn)程阻塞的信號集也叫信號掩碼,類型為sigset_t。每個(gè)進(jìn)程都有自己 的信號掩碼,且創(chuàng)建子進(jìn)程時(shí),子進(jìn)程會繼承父進(jìn)程的信號掩碼。
2. 信號阻塞和忽略的區(qū)別
  阻塞的概念與忽略信號是不同的:操作系統(tǒng)在信號被進(jìn)程解除阻塞之前不會將信號傳遞出去,被阻塞的信號也不會影響進(jìn)程的行為,信號只是暫時(shí)被阻止傳遞;當(dāng)進(jìn)程忽略一個(gè)信號時(shí),信號會被傳遞出去,但進(jìn)程將信號丟棄。
3. 信號集的操作
  信號集可以由以下幾個(gè)函數(shù)操作:
  int sigemptyset(sigset_t *set); //清空信號集
  int sigfillset(sigset_t *set); //將所有信號填充進(jìn)set中
  int sigaddset(sigset_t *set, int signum); //往set中添加信號signum
  int sigdelset(sigset_t *set, int signum); //從set中移除信號signum
  int sigismember(const sigset_t *set, int signum); //判斷signnum是不是包含在set中,在返回1,不在返回0
  初始化往往可以用sigemptyset()將信號集清空,再用sigaddset()向信號集中添加信號;或者可以使用sigfillset()將所有信號添加到信號集,再用sigdelset()將某信號從中刪除掉。
4. sigprocmask()介紹
  可以使用函數(shù)sigprocmask()來檢查或者修改進(jìn)程的信號掩碼。函數(shù)信息如下:
  #include <signal.h>
  int sigprocmask ( int how, const sigset_t *restrict set,
  sigset_t *restrict old );
  參數(shù)how 是一個(gè)整數(shù),說明信號掩碼的修改方式:
  SIG_BLOCK --- 將set指向的信號集中的信號添加到當(dāng)前阻塞信號集中;
  SIG_UNBLOCK --- 從當(dāng)前阻塞信號集中移除set指向的信號集中的信號;
  SIG_SETMASK --- 指定set所指向的信號集為當(dāng)前阻塞信號集。
  此外,如果參數(shù)set 為NULL, 說明不需要修改,如果old 為NULL,sigprocmask會將修改之前的信號集放在*old 之中返回。
5.sigaction()回顧
  在前面有用過sigaction()函數(shù):
  include <signal.h>
  int sigaction(int signum,const struct sigaction *act,
  const struct sigaction *oldact);
  該函數(shù)是用于注冊一個(gè)信號處理函數(shù)。參數(shù)結(jié)構(gòu)體sigaction與函數(shù)同名,具體信息如下:
  struct sigaction {
  void (*sa_handler)(int); //老類型的信號處理函數(shù)指針
  void (*sa_sigaction)(int, siginfo_t *, void *);//新類型的信號處理函數(shù)指針
  sigset_t sa_mask; //將要被阻塞的信號集合
  int sa_flags; //信號處理方式掩碼
  void (*sa_restorer)(void); //保留
  }
  5.1 sa_handler:一個(gè)函數(shù)指針,用于指向原型為void handler(int)的信號處理函數(shù)地址(老類型的信號處理函數(shù));
  5.2 sa_sigaction:也是一個(gè)函數(shù)指針,用于指向原型為:
  void handler(int (新類型的信號處理函數(shù));
  三個(gè)參數(shù)的含義為:
  iSignNum:傳入的信號
  pSignInfo:與該信號相關(guān)的一些信息,它是個(gè)結(jié)構(gòu)體
  pReserved:保留,現(xiàn)沒用
   5.3 sa_handler和sa_sigaction只應(yīng)該有一個(gè)生效,如果想采用老的信號處理機(jī)制,就應(yīng)該讓sa_handler指向正確的信號處理函數(shù); 否則應(yīng)該讓sa_sigaction指向正確的信號處理函數(shù),并且讓字段sa_flags包含SA_SIGINFO選項(xiàng)。
  5.4 sa_mask是一個(gè)包含信號集合的結(jié)構(gòu)體,該結(jié)構(gòu)體內(nèi)的信號表示在進(jìn)行信號處理時(shí),將要被阻塞的信號。該信號集可以用前面標(biāo)題3提到的5個(gè)函數(shù)來進(jìn)行操作。
  5.5 字段sa_flags是一組掩碼的合成值,指示信號處理時(shí)所應(yīng)該采取的一些行為,各掩碼的含義為:
  (1)SA_RESETHAND ---處理完畢要捕捉的信號后,將自動(dòng)撤消信號處理函數(shù)的注冊,即必須再重新注冊信號處理函數(shù),才能繼續(xù)處理接下來產(chǎn)生的信號。
  (2)SA_NODEFER ---在處理信號時(shí),如果又發(fā)生了其它的信號,則立即進(jìn)入其它信號的處理,等其它信號處理完畢后,再繼續(xù)處理當(dāng)前的信號,即遞規(guī)地處理。如果sa_flags包含了該掩碼,則結(jié)構(gòu)體sigaction的sa_mask將無效;
  (3)SA_RESTART--- 如果在發(fā)生信號時(shí),程序正阻塞在某個(gè)系統(tǒng)調(diào)用,例如調(diào)用read()函數(shù),則在處理完畢信號后,接著從阻塞的系統(tǒng)返回。該掩碼符合普通的程序處理流程,所以一般來說,應(yīng)該設(shè)置該掩碼,否則信號處理完后,阻塞的系統(tǒng)調(diào)用將會返回失敗;
  (4)SA_SIGINFO ---指示結(jié)構(gòu)體的信號處理函數(shù)指針是哪個(gè)有效,如果sa_flags包含該掩碼,則sa_sigactiion指針有效,否則是sa_handler指針有效。
  需要注意的是:
   函數(shù)sigprocmask是全程阻塞,在sigprocmask中設(shè)置了阻塞集合后,被阻塞的信號將不能再被信號處理函數(shù)捕捉,直到重新設(shè)置阻塞信號 集合。而在sigaction()注冊信號處理函數(shù)時(shí),選擇阻塞的信號集只是在處理捕捉的信號時(shí),才對指定的其他信號進(jìn)行阻塞。
6、信號未決
sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,卻被阻塞的任何信號,在set指向的信號集中返回結(jié)果。
sigsuspend(const sigset_t *mask))用于在接收到某個(gè)信號之前, 臨時(shí)用mask替換進(jìn)程的信號掩碼, 并暫停進(jìn)程執(zhí)行,直到收到信號為止。sigsuspend 返回后將恢復(fù)調(diào)用之前的信號掩碼。信號處理函數(shù)完成后,進(jìn)程將繼續(xù)執(zhí)行。該系統(tǒng)調(diào)用始終返回-1,并將errno配置為EINTR。

計(jì)時(shí)器與信號

睡眠函數(shù)
Linux下有兩個(gè)睡眠函數(shù),原型為:
#include <unistd.h>
   unsigned int sleep(unsigned int seconds);
   void usleep(unsigned long usec);
函數(shù)sleep讓進(jìn)程睡眠seconds秒,函數(shù)usleep讓進(jìn)程睡眠usec毫秒。
sleep睡眠函數(shù)內(nèi)部是用信號機(jī)制進(jìn)行處理的,用到的函數(shù)有:
   #include <unistd.h>
   unsigned int alarm(unsigned int seconds); //告知自身進(jìn)程,要進(jìn)程在seconds秒后自動(dòng)產(chǎn)生一個(gè)//SIGALRM的信號,
   int pause(void);                       //將自身進(jìn)程掛起,直到有信號發(fā)生時(shí)才從pause返回

示例:模擬睡眠3秒:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void SignHandler(int iSignNo)
{
    printf("signal:%d\n",iSignNo);
}

int main()
{
    signal(SIGALRM,SignHandler);
    alarm(3);
    printf("Before pause().\n");
    pause();
    printf("After pause().\n");
    return 0;
}
注意:因?yàn)閟leep在內(nèi)部是用alarm實(shí)現(xiàn)的,所以在程序中最好不要sleep與alarm混用,以免造成混亂。

時(shí)鐘處理
Linux為每個(gè)進(jìn)程維護(hù)3個(gè)計(jì)時(shí)器,分別是真實(shí)計(jì)時(shí)器、虛擬計(jì)時(shí)器和實(shí)用計(jì)時(shí)器。
真實(shí)計(jì)時(shí)器計(jì)算的是程序運(yùn)行的實(shí)際時(shí)間;
虛擬計(jì)時(shí)器計(jì)算的是程序運(yùn)行在用戶態(tài)時(shí)所消耗的時(shí)間(可認(rèn)為是實(shí)際時(shí)間減掉(系統(tǒng)調(diào)用和程序睡眠所消耗)的時(shí)間);
實(shí)用計(jì)時(shí)器計(jì)算的是程序處于用戶態(tài)和處于內(nèi)核態(tài)所消耗的時(shí)間之和。
例如:有一程序運(yùn)行,在用戶態(tài)運(yùn)行了5秒,在內(nèi)核態(tài)運(yùn)行了6秒,還睡眠了7秒,則真實(shí)計(jì)算器計(jì)算的結(jié)果是18秒,虛擬計(jì)時(shí)器計(jì)算的是5秒,實(shí)用計(jì)時(shí)器計(jì)算的是11秒。
用指定的初始間隔和重復(fù)間隔時(shí)間為進(jìn)程設(shè)定好一個(gè)計(jì)時(shí)器后,該計(jì)時(shí)器就會定時(shí)地向進(jìn)程發(fā)送時(shí)鐘信號。3個(gè)計(jì)時(shí)器發(fā)送的時(shí)鐘信號分別為:SIGALRM,SIGVTALRM和SIGPROF。
用到的函數(shù)與數(shù)據(jù)結(jié)構(gòu):
#include <sys/time.h>

//獲取計(jì)時(shí)器的設(shè)置
//which指定哪個(gè)計(jì)時(shí)器,可選項(xiàng)為ITIMER_REAL(真實(shí)計(jì)時(shí)器)、ITIMER_VITUAL(虛擬計(jì)時(shí)器、ITIMER_PROF(實(shí)用計(jì)時(shí)器))
//value為一結(jié)構(gòu)體的傳出參數(shù),用于傳出該計(jì)時(shí)器的初始間隔時(shí)間和重復(fù)間隔時(shí)間
//如果成功,返回0,否則-1
int getitimer(int which, struct itimerval *value);

//設(shè)置計(jì)時(shí)器
//which指定哪個(gè)計(jì)時(shí)器,可選項(xiàng)為ITIMER_REAL(真實(shí)計(jì)時(shí)器)、ITIMER_VITUAL(虛擬計(jì)時(shí)器、ITIMER_PROF(實(shí)用計(jì)時(shí)器))
//value為一結(jié)構(gòu)體的傳入?yún)?shù),指定該計(jì)時(shí)器的初始間隔時(shí)間和重復(fù)間隔時(shí)間
//ovalue為一結(jié)構(gòu)體傳出參數(shù),用于傳出以前的計(jì)時(shí)器時(shí)間設(shè)置。
//如果成功,返回0,否則-1
int setitimer(int which, const struct itimerval *value, struct itimer val *ovalue);

struct itimerval {
struct timeval it_interval; /* next value */   //重復(fù)間隔
struct timeval it_value;    /* current value */ //初始間隔
};
struct timeval {
long tv_sec;                /* seconds */    //時(shí)間的秒數(shù)部分
long tv_usec;               /* microseconds */     //時(shí)間的微秒部分
};

示例:啟用真實(shí)計(jì)時(shí)器的進(jìn)行時(shí)鐘處理
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

void TimeInt2Obj(int imSecond,timeval *ptVal)
{
    ptVal->tv_sec=imSecond/1000;
    ptVal->tv_usec=(imSecond%1000)*1000;
}

void SignHandler(int SignNo)
{
    printf("Clock\n");
}

int main()
{
    signal(SIGALRM,SignHandler);
    itimerval tval;
    TimeInt2Obj(1,&tval.it_value);   //設(shè)初始間隔為1毫秒,注意不要為0
    TimeInt2Obj(1500,&tval.it_interval); //設(shè)置以后的重復(fù)間隔為1500毫秒
    setitimer(ITIMER_REAL,&tval,NULL);
    while(getchar()!=EOF);
    return 0;
}


信號生命周期

從信號發(fā)送到信號處理函數(shù)的執(zhí)行完畢
對于一個(gè)完整的信號生命周期(從信號發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來說,能夠分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來刻畫:信號誕生;信號在進(jìn)程中注冊完畢;信號在進(jìn)程中的注銷完畢;信號處理函數(shù)執(zhí)行完畢。相鄰兩個(gè)事件的時(shí)間間隔構(gòu)成信號生命周期的一個(gè)階段。

下面闡述四個(gè)事件的實(shí)際意義:
1. 信號"誕生"。信號的誕生指的是觸發(fā)信號的事件發(fā)生(如檢測到硬件異常、定時(shí)器超時(shí)連同調(diào)用信號發(fā)送函數(shù)kill()或sigqueue()等)。
2. 信號在目標(biāo)進(jìn)程中"注冊";進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號的數(shù)據(jù)成員:
3. struct sigpending pending:
4. struct sigpending{
5. struct sigqueue *head, **tail;
6. sigset_t signal;
7. };
第三個(gè)成員是進(jìn)程中任何未決信號集,第一、第二個(gè)成員分別指向一個(gè)sigqueue類型的結(jié)構(gòu)鏈(稱之為"未決信號信息鏈")的首尾,信息鏈中的每個(gè)sigqueue結(jié)構(gòu)刻畫一個(gè)特定信號所攜帶的信息,并指向下一個(gè)sigqueue結(jié)構(gòu):
struct sigqueue{
struct sigqueue *next;
siginfo_t info;
}
信號在進(jìn)程中注冊指的就是信號值加入到進(jìn)程的未決信號集中(sigpending結(jié)構(gòu)的第二個(gè)成員sigset_t signal),并且信號所攜帶的信息被保留到未決信號信息鏈的某個(gè)sigqueue結(jié)構(gòu)中。只要信號在進(jìn)程的未決信號集中,表明進(jìn)程已知道這些信號的 存在,但還沒來得及處理,或該信號被進(jìn)程阻塞。
注:
當(dāng)一個(gè)實(shí)時(shí)信號發(fā)送給一個(gè)進(jìn)程時(shí),不管該信號是否已在進(jìn)程中注冊,都會被再注冊一次,因此,信號不會丟失,因此,實(shí)時(shí)信號又叫做"可靠信號"。這意味著 同一個(gè)實(shí)時(shí)信號能夠在同一個(gè)進(jìn)程的未決信號信息鏈中占有多個(gè)sigqueue結(jié)構(gòu)(進(jìn)程每收到一個(gè)實(shí)時(shí)信號,都會為他分配一個(gè)結(jié)構(gòu)來登記該信號信息,并把 該結(jié)構(gòu)添加在未決信號鏈尾,即任何誕生的實(shí)時(shí)信號都會在目標(biāo)進(jìn)程中注冊);
當(dāng)一個(gè)非實(shí)時(shí)信號發(fā)送給一個(gè)進(jìn)程時(shí),假如該信號已在進(jìn)程中注冊,則該信號將被丟棄,造成信號丟失。因此,非實(shí)時(shí)信號又叫做"不可靠信號"。這 意味著同一個(gè)非實(shí)時(shí)信號在進(jìn)程的未決信號信息鏈中,至多占有一個(gè)sigqueue結(jié)構(gòu)(一個(gè)非實(shí)時(shí)信號誕生后,(1)、假如發(fā)現(xiàn)相同的信號已在目標(biāo)結(jié)構(gòu) 中注冊,則不再注冊,對于進(jìn)程來說,相當(dāng)于不知道本次信號發(fā)生,信號丟失;(2)、假如進(jìn)程的未決信號中沒有相同信號,則在進(jìn)程中注冊自己)。
8. 信號在進(jìn)程中的注銷。在目標(biāo)進(jìn)程執(zhí)行過程中,會檢測是否有信號等待處理(每次從系統(tǒng)空間返回到用戶空間時(shí)都做這樣的檢查)。假如存在未決信號等待處理且該 信號沒有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號處理函數(shù)前,進(jìn)程會把信號在未決信號鏈中占有的結(jié)構(gòu)卸掉。是否將信號從進(jìn)程未決信號集中刪除對于實(shí)時(shí)和非實(shí)時(shí)信號 是不同的。對于非實(shí)時(shí)信號來說,由于在未決信號信息鏈中最多只占用一個(gè)sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信號在進(jìn)程未決信號集中刪除(信 號注銷完畢);而對于實(shí)時(shí)信號來說,可能在未決信號信息鏈中占用多個(gè)sigqueue結(jié)構(gòu),因此應(yīng)該針對占用sigqueue結(jié)構(gòu)的數(shù)目區(qū)別對待:假如只 占用一個(gè)sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號一次),則應(yīng)該把信號在進(jìn)程的未決信號集中刪除(信號注銷完畢)。否則,不應(yīng)該在進(jìn)程的未決信號集中刪除 該信號(信號注銷完畢)。
進(jìn)程在執(zhí)行信號相應(yīng)處理函數(shù)之前,首先要把信號在進(jìn)程中注銷。
9. 信號生命終止。進(jìn)程注銷信號后,立即執(zhí)行相應(yīng)的信號處理函數(shù),執(zhí)行完畢后,信號的本次發(fā)送對進(jìn)程的影響完全結(jié)束。
注:
1)信號注冊和否,和發(fā)送信號的函數(shù)(如kill()或sigqueue()等)連同信號安裝函數(shù)(signal()及sigaction()) 無關(guān),只和信號值有關(guān)(信號值小于SIGRTMIN的信號最多只注冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信號,只要被進(jìn)程接收到就被 注冊)。
2)在信號被注銷到相應(yīng)的信號處理函數(shù)執(zhí)行完畢這段時(shí)間內(nèi),假如進(jìn)程又收到同一信號多次,則對實(shí)時(shí)信號來說,每一次都會在進(jìn)程中注冊;而對于非實(shí)時(shí)信號來說,無論收到多少次信號,都會視為只收到一個(gè)信號,只在進(jìn)程中注冊一次。
信號編程注意事項(xiàng)

1. 防止不該丟失的信號丟失。假如對八中所提到的信號生命周期理解深刻的話,很容易知道信號會不會丟失,連同在哪里丟失。
2. 程式的可移植性
考慮到程式的可移植性,應(yīng)該盡量采用POSIX信號函數(shù),POSIX信號函數(shù)主要分為兩類:
o POSIX 1003.1信號函數(shù): Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、 sigismember()、sigpending()、sigprocmask()、sigsuspend()。
o POSIX 1003.1b信號函數(shù)。POSIX 1003.1b在信號的實(shí)時(shí)性方面對POSIX 1003.1做了擴(kuò)展,包括以下三個(gè)函數(shù): sigqueue()、sigtimedwait()、sigwaitinfo()。其中,sigqueue主要針對信號發(fā)送,而 sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函數(shù),后面有相應(yīng)實(shí)例。
o #include
o int sigwaitinfo(sigset_t *set, siginfo_t *info).
該函數(shù)和sigsuspend()類似,阻塞一個(gè)進(jìn)程直到特定信號發(fā)生,但信號到來時(shí)不執(zhí)行信號處理函數(shù),而是返回信號值。因此為了避免執(zhí)行相應(yīng)的信號處理函數(shù),必須在調(diào)用該函數(shù)前,使進(jìn)程屏蔽掉set指向的信號,因此調(diào)用該函數(shù)的典型代碼是:
sigset_t newmask;
int rcvd_sig;
siginfo_t info;

sigemptyset(&newmask);
sigaddset(&newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &newmask, NULL);
rcvd_sig = sigwaitinfo(&newmask, &info)
if (rcvd_sig == -1) {
..
}
調(diào)用成功返回信號值,否則返回-1。sigtimedwait()功能相似,只但是增加了一個(gè)進(jìn)程等待的時(shí)間。
3. 程式的穩(wěn)定性。
為了增強(qiáng)程式的穩(wěn)定性,在信號處理函數(shù)中應(yīng)使用可重入函數(shù)。
信號處理程式中應(yīng)當(dāng)使用可再入(可重入)函數(shù)(注:所謂可重入函數(shù)是指一個(gè)能夠被多個(gè)任務(wù)調(diào)用的過程,任務(wù)在調(diào)用時(shí)不必?fù)?dān)心數(shù)據(jù)是否會出錯(cuò))。因?yàn)檫M(jìn)程在 收到信號后,就將跳轉(zhuǎn)到信號處理函數(shù)去接著執(zhí)行。假如信號處理函數(shù)中使用了不可重入函數(shù),那么信號處理函數(shù)可能會修改原來進(jìn)程中不應(yīng)該被修改的數(shù)據(jù),這樣 進(jìn)程從信號處理函數(shù)中返回接著執(zhí)行時(shí),可能會出現(xiàn)不可預(yù)料的后果。不可再入函數(shù)在信號處理函數(shù)中被視為不安全函數(shù)。
滿足下列條件的函數(shù)多數(shù)是不可再入的:(1)使用靜態(tài)的數(shù)據(jù)結(jié)構(gòu),如getlogin(),gmtime(),getgrgid(), getgrnam(),getpwuid()連同getpwnam()等等;(2)函數(shù)實(shí)現(xiàn)時(shí),調(diào)用了malloc()或free()函數(shù);(3)實(shí)現(xiàn) 時(shí)使用了標(biāo)準(zhǔn)I/O函數(shù)的。The Open Group視下列函數(shù)為可再入的:
_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、 cfsetospeed()、chdir()、chmod()、chown()、close()、creat()、dup()、dup2()、 execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、 kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid ()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、 sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、 stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、 tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。
即使信號處理函數(shù)使用的都是"安全函數(shù)",同樣要注意進(jìn)入處理函數(shù)時(shí),首先要保存errno的值,結(jié)束時(shí),再恢復(fù)原值。因?yàn)椋盘柼幚磉^程中,errno 值隨時(shí)可能被改變。另外,longjmp()連同siglongjmp()沒有被列為可再入函數(shù),因?yàn)椴荒艽_保緊接著兩個(gè)函數(shù)的其他調(diào)用是安全的。
信號應(yīng)用實(shí)例

信號的安裝(配置信號關(guān)聯(lián)動(dòng)作)
linux下的信號應(yīng)用并沒有想象的那么恐怖,程式員所要做的最多只有三件事情:
1. 安裝信號(推薦使用sigaction());
2. 實(shí)現(xiàn)三參數(shù)信號處理函數(shù),handler(int signal,struct siginfo *info, void *);
3. 發(fā)送信號,推薦使用sigqueue()。
實(shí)際上,對有些信號來說,只要安裝信號就足夠了(信號處理方式采用缺省或忽略)。其他可能要做的無非是和信號集相關(guān)的幾種操作。
實(shí)例一:信號發(fā)送及處理
實(shí)現(xiàn)一個(gè)信號接收程式sigreceive(其中信號安裝由sigaction())。
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);

sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;

if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}

while(1)
{
sleep(2);
printf("wait for the signal\n");
}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d", signum);
sleep(5);
}
說明,命令行參數(shù)為信號值,后臺運(yùn)行sigreceive signo &,可獲得該進(jìn)程的ID,假設(shè)為pid,然后再另一終端上運(yùn)行kill -s signo pid驗(yàn)證信號的發(fā)送接收及處理。同時(shí),可驗(yàn)證信號的排隊(duì)問題。
注:能夠用sigqueue實(shí)現(xiàn)一個(gè)命令行信號發(fā)送程式sigqueuesend
實(shí)例二:信號傳遞附加信息
主要包括兩個(gè)實(shí)例:
1. 向進(jìn)程本身發(fā)送信號,并傳遞指針參數(shù);
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
union sigval mysigval;
 int i;
 int sig;
 pid_t pid;
 char data[10];
 memset(data,0,sizeof(data));
 for(i=0;i < 5;i )
 data[i]='2';
 mysigval.sival_ptr=data;

 sig=atoi(argv[1]);
 pid=getpid();

 sigemptyset(&act.sa_mask);
 act.sa_sigaction=new_op;//三參數(shù)信號處理函數(shù)
 act.sa_flags=SA_SIGINFO;//信息傳遞開關(guān)
 if(sigaction(sig,&act,NULL) < 0)
 {
 printf("install sigal error\n");
 }
 while(1)
 {
 sleep(2);
 printf("wait for the signal\n");
 sigqueue(pid,sig,mysigval);//向本進(jìn)程發(fā)送信號,并傳遞附加信息
 }

 }

 void new_op(int signum,siginfo_t *info,void *myact)//三參數(shù)信號處理函數(shù)的實(shí)現(xiàn)
 {
 int i;
 for(i=0;i<10;i )
 {
 printf("%c\n ",(*( (char*)((*info).si_ptr) i)));
 }
 printf("handle signal %d over;",signum);
 }
這個(gè)例子中,信號實(shí)現(xiàn)了附加信息的傳遞,信號究竟如何對這些信息進(jìn)行處理則取決于具體的應(yīng)用。
2、 不同進(jìn)程間傳遞整型參數(shù):把1中的信號發(fā)送和接收放在兩個(gè)程式中,并且在發(fā)送過程中傳遞整型參數(shù)。
信號接收程式:
#include
#include
#include
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
pid_t pid;

pid=getpid();
sig=atoi(argv[1]);

sigemptyset(&act.sa_mask);
act.sa_sigaction=new_op;
act.sa_flags=SA_SIGINFO;
if(sigaction(sig,&act,NULL)<0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(2);
printf("wait for the signal\n");
}

}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("the int value is %d \n",info->si_int);
}
信號發(fā)送程式:命令行第二個(gè)參數(shù)為信號值,第三個(gè)參數(shù)為接收進(jìn)程ID。
main(int argc,char**argv)
{
pid_t pid;
int signum;
union sigval mysigval;

signum=atoi(argv[1]);
pid=(pid_t)atoi(argv[2]);
mysigval.sival_int=8;//不代表具體含義,只用于說明問題

if(sigqueue(pid,signum,mysigval)==-1)
printf("send error\n");
sleep(2);
}
實(shí)例三:信號阻塞及信號集操作
#include "signal.h"
#include "unistd.h"
static void my_op(int);
main()
{
sigset_t new_mask,old_mask,pending_mask;
struct sigaction act;

sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=(void*)my_op;
if(sigaction(SIGRTMIN 10,&act,NULL))
printf("install signal SIGRTMIN 10 error\n");

sigemptyset(&new_mask);
sigaddset(&new_mask,SIGRTMIN 10);
if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))
printf("block signal SIGRTMIN 10 error\n");

sleep(10);
printf("now begin to get pending mask and unblock SIGRTMIN 10\n");
if(sigpending(&pending_mask)<0)
printf("get pending mask error\n");
if(sigismember(&pending_mask,SIGRTMIN 10))
printf("signal SIGRTMIN 10 is pending\n");

if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)
printf("unblock signal error\n");
printf("signal unblocked\n");

sleep(10);
}
static void my_op(int signum)
{
printf("receive signal %d \n",signum);
}

編譯該程式,并以后臺方式運(yùn)行。在另一終端向該進(jìn)程發(fā)送信號(運(yùn)行kill -s 42 pid,SIGRTMIN 10為42),查看結(jié)果能夠看出幾個(gè)關(guān)鍵函數(shù)的運(yùn)行機(jī)制,信號集相關(guān)操作比較簡單。
注:在上面幾個(gè)實(shí)例中,使用了printf()函數(shù),只是作為診斷工具,pringf()函數(shù)是不可重入的,不應(yīng)在信號處理函數(shù)中使用。

用sigqueue實(shí)現(xiàn)的命令行信號發(fā)送程式sigqueuesend,命令行第二個(gè)參數(shù)是發(fā)送的信號值,第三個(gè)參數(shù)是接收該信號的進(jìn)程ID,能夠配合實(shí)例一使用:
int main(int argc,char**argv)
{
pid_t pid;
int sig;
sig=atoi(argv[1]);
pid=atoi(argv[2]);
sigqueue(pid,sig,NULL);
sleep(2);
}

posted on 2011-07-27 16:28 艾斯維亞 閱讀(10323) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲精品欧美专区| 欧美亚日韩国产aⅴ精品中极品| 亚洲欧美资源在线| 亚洲视频一区在线| 亚洲午夜免费福利视频| 亚洲永久免费| 亚洲欧美清纯在线制服| 亚洲在线成人| 久久久久天天天天| 亚洲理论在线| 欧美一区二区三区免费观看| 久久精品观看| 久久激情综合| 午夜视频在线观看一区二区| 亚洲一区二区三区精品在线| 久久精品一区二区三区不卡牛牛| 蜜臀99久久精品久久久久久软件| 亚洲国产精品成人久久综合一区| 欧美ed2k| 亚洲国产裸拍裸体视频在线观看乱了中文| 欧美国产激情二区三区| 亚洲天堂男人| 欧美国产欧美亚州国产日韩mv天天看完整 | 亚洲精品午夜| 欧美在线高清视频| 老鸭窝毛片一区二区三区| 国产精品v欧美精品∨日韩| 国产日韩在线一区| 亚洲天堂网站在线观看视频| 欧美福利电影网| 久热爱精品视频线路一| 欧美亚男人的天堂| 一区二区日韩| 亚洲美女毛片| 欧美性色视频在线| 香蕉国产精品偷在线观看不卡| 亚洲国产精品一区二区久| 欧美一区三区三区高中清蜜桃| 国产精品theporn| 午夜在线精品偷拍| 亚洲欧美日韩国产成人| 国产日韩精品一区二区三区| 午夜久久福利| 欧美一级成年大片在线观看| 国产一本一道久久香蕉| 欧美成人自拍| 99视频精品全国免费| 宅男噜噜噜66一区二区66| 国产精品国产三级国产普通话三级| 一区二区三区偷拍| 一区二区不卡在线视频 午夜欧美不卡在 | 国产精品h在线观看| 亚洲一区二区久久| 欧美成人免费观看| 老鸭窝毛片一区二区三区| 欧美视频在线观看免费| 亚洲精品美女| 一本色道久久综合亚洲91| 欧美中文字幕在线观看| 久久经典综合| 欧美日韩亚洲综合| 免费在线成人av| 韩国av一区二区| 一区二区福利| 亚洲毛片视频| 欧美成人一品| 亚洲高清视频中文字幕| 国产无遮挡一区二区三区毛片日本| 国产精品亚洲产品| 99re视频这里只有精品| 亚洲日本aⅴ片在线观看香蕉| 香蕉久久一区二区不卡无毒影院 | 红桃视频亚洲| 亚洲一区二区三区高清| 亚洲香蕉在线观看| 国产精品国产a级| 欧美一区影院| 欧美亚洲视频一区二区| 欧美视频二区36p| 这里是久久伊人| 久久久久国色av免费观看性色| 国产精品午夜电影| 久久久久国产精品一区| 裸体一区二区| 亚洲在线视频观看| 尤物yw午夜国产精品视频| 美女诱惑一区| 亚洲男女自偷自拍| 欧美激情精品久久久久久| 亚洲视频图片小说| 激情久久久久| 国产精品进线69影院| 久久婷婷国产综合精品青草| 亚洲第一福利视频| 欧美中文字幕在线播放| 一区二区高清视频在线观看| 国产主播一区二区三区| 欧美性大战久久久久久久| 久久精品噜噜噜成人av农村| 亚洲精品国产精品乱码不99按摩| 亚洲欧美日韩国产一区| 亚洲精品中文字幕在线| 国产日韩1区| 国产欧美二区| 国产精品v日韩精品v欧美精品网站| 久久久久看片| 久久riav二区三区| 久久精品国产视频| 一区二区不卡在线视频 午夜欧美不卡'| 欧美午夜电影完整版| 欧美人与性动交a欧美精品| 美女脱光内衣内裤视频久久网站| 亚洲主播在线观看| 欧美在线视频导航| 久久精品亚洲精品国产欧美kt∨| 国产欧美日韩不卡| 国产精品免费一区豆花| 欧美久久在线| 国产精品yjizz| 美女免费视频一区| 亚洲精品日本| 亚洲自拍高清| 久久综合免费视频影院| 欧美承认网站| 国产精品s色| 黑人巨大精品欧美一区二区小视频| 国产在线成人| 99re成人精品视频| 亚洲欧美区自拍先锋| 狂野欧美一区| 99国产精品久久久| 久久久久久久999精品视频| 欧美高清视频一区| 国产亚洲成av人在线观看导航 | 在线播放日韩| 亚洲网站在线观看| 蜜桃久久av| 欧美一区激情| 国产精品丝袜白浆摸在线| 亚洲电影在线看| 久久久久久久尹人综合网亚洲 | 99精品国产一区二区青青牛奶 | 国产性色一区二区| 中文国产成人精品久久一| 免费在线欧美黄色| 欧美在线亚洲在线| 国产日韩欧美一二三区| 亚洲永久视频| 午夜欧美精品久久久久久久| 91久久国产自产拍夜夜嗨 | 欧美性久久久| 中文在线不卡| 99精品国产在热久久下载| 欧美wwwwww| 99视频精品在线| 亚洲最新色图| 国产乱码精品一区二区三| 午夜伦欧美伦电影理论片| 午夜久久影院| 亚洲欧洲综合另类| 一本色道久久88综合日韩精品| 国产精品高潮呻吟视频| 欧美一区免费视频| 久久免费视频这里只有精品| 91久久国产精品91久久性色| 麻豆国产va免费精品高清在线| 久久久91精品国产一区二区三区| 亚洲国产精品专区久久| 亚洲视频一二区| 亚洲国内欧美| 尤物九九久久国产精品的分类| 亚洲激情电影中文字幕| 欧美日韩三级一区二区| 久久久久国产精品人| 欧美国产另类| 久色成人在线| 国产私拍一区| 99国产麻豆精品| 日韩视频一区二区| 免费欧美在线| 欧美ed2k| 亚洲激情欧美| 欧美精品久久久久久| 亚洲国产导航| 亚洲精品在线一区二区| 欧美v亚洲v综合ⅴ国产v| 免费在线欧美黄色| 亚洲高清免费视频| 模特精品在线| 亚洲毛片在线看| 亚洲在线观看免费| 国产日韩欧美在线播放不卡| 亚洲私人影院| 久久人人爽人人| 精品成人乱色一区二区| 国产精品免费一区二区三区观看| 亚洲欧洲一区二区三区在线观看| 一本色道**综合亚洲精品蜜桃冫| 欧美三级精品| 美女在线一区二区|