天天看點

Linux信号實踐(2) --信号分類

不可靠信号

Linux信号機制基本上是從UNIX系統中繼承過來的。早期UNIX系統中的信号機制比較簡單和原始,後來在實踐中暴露出一些問題,它的主要問題是:

   1.程序每次處理信号後,就将對信号的響應設定為預設動作。在某些情況下,将導緻對信号的錯誤處理;是以,使用者如果不希望這樣的操作,那麼就要在信号處理函數結尾再一次調用signal(),重新安裝該信号。

   2.是以導緻, 早期UNIX下的不可靠信号主要指的是程序可能對信号做出錯誤的反應以及信号可能丢失。 

Linux支援不可靠信号,但是對不可靠信号機制做了改進:在調用完信号處理函數後,不必重新調用該信号的安裝函數(信号安裝函數是在可靠機制上的實作)。是以,Linux下的不可靠信号問題主要指的是信号可能丢失。

可靠信号

随着時間的發展,實踐證明,有必要對信号的原始機制加以改進和擴充。是以,後來出現的各種UNIX版本分别在這方面進行了研究,力圖實作"可靠信号"。由于原來定義的信号已有許多應用,不好再做改動,最終隻好又新增加了一些信号(SIGRTMIN ~ SIGRTMAX),并在一開始就把它們定義為可靠信号,這些信号支援排隊,不會丢失。同時,信号的發送和安裝也出現了新版本:信号發送函數sigqueue()及信号安裝函數sigaction()。

sigaction和signal函數都是調用核心服務do_signal函數;[核心服務函數,應用程式無法調用該函數]

早期UNIX系統隻定義了31種信号,而Linux 3.x支援64種信号,編号1-64(SIGRTMIN=34,SIGRTMAX=64),将來可能進一步增加,這需要得到核心的支援。 前31種信号已經有了預定義值,每個信号有了确定的用途及含義,并且每種信号都有各自的預設動作。如按鍵盤的CTRL+C時,會産生SIGINT信号,對該信号的預設反應就是程序終止。後32個信号表示實時信号,等同于可靠信号。這保證了發送的多個實時信号都被接收。實時信号是POSIX标準的一部分,可用于應用程序。

非實時信号都不支援排隊,都是不可靠信号;實時信号都支援排隊,都是可靠信号。

1.kill

kill既可以向自身發送信号,也可以向其他程序發送信号 

signo參數組合情況解釋:

   pid>0 将信号sig發給pid程序

   pid=0 将信号sig發給同組程序

   pid=-1 将信号sig發送給所有程序,調用者程序有權限發送的每一個程序(除了1号程序之外,還有它自身)

   pid<-1 将信号sig發送給程序組是pid(絕對值)的每一個程序

注意:如果在fork之前安裝信号,則子程序可以繼承信号。

Sleep遇上signal,子程序向父程序發送信号,sleep函數的幾點說明

   1)sleep函數作用,讓程序睡眠。

   2)能被信号打斷,然後處理信号函數以後,就不再睡眠了。直接向下執行代碼

   3)sleep函數的傳回值,是剩餘的秒數

Man手冊顯示:

RETURN VALUE

       Zero if the requested time has elapsed, or the number of  seconds  left to sleep, 

if the call was interrupted by a signal handler.

2.raise

給自己發送信号。raise(sig)等價于kill(getpid(), sig);

3.killpg

給程序組發送信号。killpg(pgrp, sig)等價于kill(-pgrp, sig);

4.sigqueue

給程序發送信号,支援排隊,可以附帶資訊。

int pause(void);

将程序置為可中斷睡眠狀态。然後它調用核心函數schedule(),使Linux程序排程器找到另一個程序來運作。

pause使調用者程序挂起,直到一個信号被捕獲

alarm函數,設定一個鬧鐘延遲發送SIGALRM信号(告訴Linux核心n秒中以後,發送SIGALRM信号);

手冊描述-DESCRIPTION

       alarm() arranges for a SIGALRM signal to be delivered to the process in seconds seconds.

       If seconds is zero, no new alarm() is scheduled.

       In any event any previously set alarm() is cancelled.

  所謂可重入函數是指一個可以被多個任務調用的過程,任務在調用時不必擔心資料是否會出錯。因為程序在收到信号後,就将跳轉到信号處理函數去接着執行。如果信号處理函數中使用了不可重入函數,那麼信号處理函數可能會修改原來程序中不應該被修改的資料,這樣程序從信号處理函數中傳回接着執行時,可能會出現不可預料的後果。不可重入函數在信号處理函數中被視為不安全函數。

  為了增強程式的穩定性,在信号處理函數中應使用可重入函數。 

輸出結果示範:

Linux信号實踐(2) --信号分類

原因分析:

可以将語句g_teacher = zero分解為:

        g_teacher.a = zero.a;

        g_teacher.b = zero.b;

        g_teacher.c = zero.c;

        g_teacher.d = zero.d;

是以, 在這四條語句執行的中間, 如果此時SIGALRM信号到達(中斷到達), 則g_teacher中的一些資料會是新值, 而有些卻是以前留下的髒值, 究其原始則是g_teacher = zero不是原子操作, 而信号處理函數onSigAlarm卻又通路了全局變量g_teacher. 

如果将兩條printf封裝成一個函數

然後在onSigAlarm中調用, 則unsafe_function函數就成了不可重入函數(其實printf就是不可重入函數), 是以, 在信号響應函數中, 盡量不要調用不可重入函數;

不可重入函數

滿足下列條件的函數多數是不可重入的:

  (1)使用靜态資料結構,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;

  (2)函數實作時,調用了malloc()或者free()函數;

  (3)實作時使用了标準I/O函數

附-man 7 signal可以檢視那些函數是可重入和不可重入的.

繼續閱讀