weidagang2046的專欄

          物格而后知致
          隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
          數(shù)據(jù)加載中……

          Linux環(huán)境進(jìn)程間通信(二):信號(上)

          linux信號機(jī)制遠(yuǎn)遠(yuǎn)比想象的復(fù)雜,本文力爭用最短的篇幅,對該機(jī)制做了深入細(xì)致的分析。信號應(yīng)用實(shí)例將在信號(下)中給出。

          一、信號及信號來源

          信號本質(zhì)

          信號是在軟件層次上對中斷機(jī)制的一種模擬,在原理上,一個進(jìn)程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進(jìn)程不必通過任何操作來等待信號的到達(dá),事實(shí)上,進(jìn)程也不知道信號到底什么時候到達(dá)。

          信號是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號的進(jìn)程有哪些事情發(fā)生了。信號機(jī)制經(jīng)過POSIX實(shí)時擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。

          信號來源

          信號事件的發(fā)生有兩個來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障);軟件來源,最常用發(fā)送信號的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer以及sigqueue函數(shù),軟件來源還包括一些非法運(yùn)算等操作。

          二、信號的種類

          可以從兩個不同的分類角度對信號進(jìn)行分類:(1)可靠性方面:可靠信號與不可靠信號;(2)與時間的關(guān)系上:實(shí)時信號與非實(shí)時信號。在《Linux環(huán)境進(jìn)程間通信(一):管道及有名管道》的附1中列出了系統(tǒng)所支持的所有信號。

          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)設(shè)置為默認(rèn)動作。在某些情況下,將導(dǎo)致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數(shù)結(jié)尾再一次調(diào)用signal(),重新安裝該信號。
          • 信號可能丟失,后面將對此詳細(xì)闡述。
            因此,早期unix下的不可靠信號主要指的是進(jìn)程可能對信號做出錯誤的反應(yīng)以及信號可能丟失。

          Linux支持不可靠信號,但是對不可靠信號機(jī)制做了改進(jìn):在調(diào)用完信號處理函數(shù)后,不必重新調(diào)用該信號的安裝函數(shù)(信號安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此,Linux下的不可靠信號問題主要指的是信號可能丟失。

          "可靠信號"

          隨著時間的發(fā)展,實(shí)踐證明了有必要對信號的原始機(jī)制加以改進(jìn)和擴(kuò)充。所以,后來出現(xiàn)的各種Unix版本分別在這方面進(jìn)行了研究,力圖實(shí)現(xiàn)"可靠信號"。由于原來定義的信號已有許多應(yīng)用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。同時,信號的發(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()的同時,仍然支持早期的signal()信號安裝函數(shù),支持信號發(fā)送函數(shù)kill()。

          注:不要有這樣的誤解:由sigqueue()發(fā)送、sigaction安裝的信號就是可靠的。事實(shí)上,可靠信號是指后來添加的新信號(信號值位于SIGRTMIN及SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠與不可靠只與信號值有關(guān),與信號的發(fā)送及安裝函數(shù)無關(guān)。目前l(fā)inux中的signal()是通過sigation()函數(shù)實(shí)現(xiàn)的,因此,即使通過signal()安裝的信號,在信號處理函數(shù)的結(jié)尾也不必再調(diào)用一次信號安裝函數(shù)。同時,由signal()安裝的實(shí)時信號支持排隊,同樣不會丟失。

          對于目前l(fā)inux的兩個信號安裝函數(shù):signal()及sigaction()來說,它們都不能把SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊。這兩個函數(shù)的最大區(qū)別在于,經(jīng)過sigaction安裝的信號都能傳遞信息給信號處理函數(shù)(對所有信號這一點(diǎn)都成立),而經(jīng)過signal安裝的信號卻不能向信號處理函數(shù)傳遞信息。對于信號發(fā)送函數(shù)來說也是一樣的。

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

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

          非實(shí)時信號都不支持排隊,都是不可靠信號;實(shí)時信號都支持排隊,都是可靠信號。

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

          進(jìn)程可以通過三種方式來響應(yīng)一個信號:(1)忽略信號,即對信號不做任何處理,其中,有兩個信號不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號。定義信號處理函數(shù),當(dāng)信號發(fā)生時,執(zhí)行相應(yīng)的處理函數(shù);(3)執(zhí)行缺省操作,Linux對每種信號都規(guī)定了默認(rèn)操作,詳細(xì)情況請參考[2]以及其它資料。注意,進(jìn)程對實(shí)時信號的缺省反應(yīng)是進(jìn)程終止。

          Linux究竟采用上述三種方式的哪一個來響應(yīng)信號,取決于傳遞給相應(yīng)API函數(shù)的參數(shù)。

          四、信號的發(fā)送

          發(fā)送信號的主要函數(shù)有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。

          1、kill()
          #include <sys/types.h>
          #include <signal.h>
          int kill(pid_t pid,int signo)
          參數(shù)pid的值 信號的接收進(jìn)程
          pid>0 進(jìn)程ID為pid的進(jìn)程
          pid=0 同一個進(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í)際不發(fā)送任何信號,但照常進(jìn)行錯誤檢查,因此,可用于檢查目標(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ū)儆谕粋€session或者同一個用戶的進(jìn)程發(fā)送信號)。

          Kill()最常用于pid>0時的信號發(fā)送,調(diào)用成功返回 0; 否則,返回 -1。注:對于pid<0時的情況,對于哪些進(jìn)程將接受信號,各種版本說法不一,其實(shí)很簡單,參閱內(nèi)核源碼kernal/signal.c即可,上表中的規(guī)則是參考red hat 7.2。

          2、raise()
          #include <signal.h>
          int raise(int signo)
          向進(jìn)程本身發(fā)送信號,參數(shù)為即將發(fā)送的信號值。調(diào)用成功返回 0;否則,返回 -1。

          3、sigqueue()
          #include <sys/types.h>
          #include <signal.h>
          int sigqueue(pid_t pid, int sig, const union sigval val)
          調(diào)用成功返回 0;否則,返回 -1。

          sigqueue()是比較新的發(fā)送信號系統(tǒng)調(diào)用,主要是針對實(shí)時信號提出的(當(dāng)然也支持前32種),支持信號帶有參數(shù),與函數(shù)sigaction()配合使用。

          sigqueue的第一個參數(shù)是指定接收信號的進(jìn)程ID,第二個參數(shù)確定即將發(fā)送的信號,第三個參數(shù)是一個聯(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()只能向一個進(jìn)程發(fā)送信號,而不能發(fā)送信號給一個進(jìn)程組。如果signo=0,將會執(zhí)行錯誤檢查,但實(shí)際上不發(fā)送任何信號,0值信號可用于檢查pid的有效性以及當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號。

          在調(diào)用sigqueue時,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ù); sigqueue()發(fā)送非實(shí)時信號時,仍然不支持排隊,即在信號處理函數(shù)執(zhí)行過程中到來的所有相同信號,都被合并為一個信號。

          4、alarm()
          #include <unistd.h>
          unsigned int alarm(unsigned int seconds)
          專門為SIGALRM信號而設(shè),在指定的時間seconds秒后,將向進(jìn)程本身發(fā)送SIGALRM信號,又稱為鬧鐘時間。進(jìn)程調(diào)用alarm后,任何以前的alarm()調(diào)用都將無效。如果參數(shù)seconds為零,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時間。
          返回值,如果調(diào)用alarm()前,進(jìn)程中已經(jīng)設(shè)置了鬧鐘時間,則返回上一個鬧鐘時間的剩余時間,否則返回0。

          5、setitimer()
          #include <sys/time.h>
          int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
          setitimer()比alarm功能強(qiáng)大,支持3種類型的定時器:

          • ITIMER_REAL: 設(shè)定絕對時間;經(jīng)過指定的時間后,內(nèi)核將發(fā)送SIGALRM信號給本進(jìn)程;
          • ITIMER_VIRTUAL 設(shè)定程序執(zhí)行時間;經(jīng)過指定的時間后,內(nèi)核將發(fā)送SIGVTALRM信號給本進(jìn)程;
          • ITIMER_PROF 設(shè)定進(jìn)程執(zhí)行以及內(nèi)核因本進(jìn)程而消耗的時間和,經(jīng)過指定的時間后,內(nèi)核將發(fā)送ITIMER_VIRTUAL信號給本進(jìn)程;

          Setitimer()第一個參數(shù)which指定定時器類型(上面三種之一);第二個參數(shù)是結(jié)構(gòu)itimerval的一個實(shí)例,結(jié)構(gòu)itimerval形式見附錄1。第三個參數(shù)可不做處理。

          Setitimer()調(diào)用成功返回0,否則返回-1。

          6、abort()
          #include <stdlib.h>
          void abort(void);

          向進(jìn)程發(fā)送SIGABORT信號,默認(rèn)情況下進(jìn)程會異常退出,當(dāng)然可定義自己的信號處理函數(shù)。即使SIGABORT被進(jìn)程設(shè)置為阻塞信號,調(diào)用abort()后,SIGABORT仍然能被進(jìn)程接收。該函數(shù)無返回值。

          五、信號的安裝(設(shè)置信號關(guān)聯(lián)動作)

          如果進(jìn)程要處理某一信號,那么就要在進(jìn)程中安裝該信號。安裝信號主要用來確定信號值及進(jìn)程針對該信號值的動作之間的映射關(guān)系,即進(jìn)程將要處理哪個信號;該信號被傳遞給進(jìn)程時,將執(zhí)行何種操作。

          linux主要有兩個函數(shù)實(shí)現(xiàn)信號的安裝:signal()、sigaction()。其中signal()在可靠信號系統(tǒng)調(diào)用的基礎(chǔ)上實(shí)現(xiàn), 是庫函數(shù)。它只有兩個參數(shù),不支持信號傳遞信息,主要是用于前32種非實(shí)時信號的安裝;而sigaction()是較新的函數(shù)(由兩個系統(tǒng)調(diào)用實(shí)現(xiàn):sys_signal以及sys_rt_sigaction),有三個參數(shù),支持信號傳遞信息,主要用來與 sigqueue() 系統(tǒng)調(diào)用配合使用,當(dāng)然,sigaction()同樣支持非實(shí)時信號的安裝。sigaction()優(yōu)于signal()主要體現(xiàn)在支持信號帶有參數(shù)。

          1、signal()
          #include <signal.h>
          void (*signal(int signum, void (*handler))(int)))(int);
          如果該函數(shù)原型不容易理解的話,可以參考下面的分解方式來理解:
          typedef void (*sighandler_t)(int);
          sighandler_t signal(int signum, sighandler_t handler));
          第一個參數(shù)指定信號的值,第二個參數(shù)指定針對前面信號值的處理,可以忽略該信號(參數(shù)設(shè)為SIG_IGN);可以采用系統(tǒng)默認(rèn)方式處理信號(參數(shù)設(shè)為SIG_DFL);也可以自己實(shí)現(xiàn)處理方式(參數(shù)指定一個函數(shù)地址)。
          如果signal()調(diào)用成功,返回最后一次為安裝信號signum而調(diào)用signal()時的handler值;失敗則返回SIG_ERR。

          2、sigaction()
          #include <signal.h>
          int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

          sigaction函數(shù)用于改變進(jìn)程接收到特定信號后的行為。該函數(shù)的第一個參數(shù)為信號的值,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號(為這兩個信號定義自己的處理函數(shù),將導(dǎo)致信號安裝錯誤)。第二個參數(shù)是指向結(jié)構(gòu)sigaction的一個實(shí)例的指針,在結(jié)構(gòu)sigaction的實(shí)例中,指定了對特定信號的處理,可以為空,進(jìn)程會以缺省方式對信號處理;第三個參數(shù)oldact指向的對象用來保存原來對相應(yīng)信號的處理,可指定oldact為NULL。如果把第二、第三個參數(shù)都設(shè)為NULL,那么該函數(shù)可用于檢查信號的有效性。

          第二個參數(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,已過時,POSIX不支持它,不應(yīng)再被使用。

          1、聯(lián)合數(shù)據(jù)結(jié)構(gòu)中的兩個元素_sa_handler以及*_sa_sigaction指定信號關(guān)聯(lián)函數(shù),即用戶指定的信號處理函數(shù)。除了可以是用戶自定義的處理函數(shù)外,還可以為SIG_DFL(采用缺省的處理方式),也可以為SIG_IGN(忽略信號)。

          2、由_sa_handler指定的處理函數(shù)只有一個參數(shù),即信號值,所以信號不能傳遞除信號值之外的任何信息;由_sa_sigaction是指定的信號處理函數(shù)帶有三個參數(shù),是為實(shí)時信號而設(shè)的(當(dāng)然同樣支持非實(shí)時信號),它指定一個3參數(shù)信號處理函數(shù)。第一個參數(shù)為信號值,第三個參數(shù)沒有使用(posix沒有規(guī)范使用該參數(shù)的標(biāo)準(zhǔn)),第二個參數(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{
          							...
          						  }...
          						... ...
          					  }
          			}
          			

          注:為了更便于閱讀,在說明問題時常把該結(jié)構(gòu)表示為附錄2所表示的形式。

          siginfo_t結(jié)構(gòu)中的聯(lián)合數(shù)據(jù)成員確保該結(jié)構(gòu)適應(yīng)所有的信號,比如對于實(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)的第四個域同樣為一個聯(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要么持有一個4字節(jié)的整數(shù)值,要么持有一個指針,這就構(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ā)送信號時,sigqueue的第三個參數(shù)就是sigval聯(lián)合數(shù)據(jù)結(jié)構(gòu),當(dāng)調(diào)用sigqueue時,該數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)就將拷貝到信號處理函數(shù)的第二個參數(shù)中。這樣,在發(fā)送信號同時,就可以讓信號傳遞一些附加信息。信號可以傳遞信息對程序開發(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)志位。另一個比較重要的標(biāo)志位是SA_SIGINFO,當(dāng)設(shè)定了該標(biāo)志位時,表示信號附帶的參數(shù)可以被傳遞到信號處理函數(shù)中,因此,應(yīng)該為sigaction結(jié)構(gòu)中的sa_sigaction指定處理函數(shù),而不應(yīng)該為sa_handler指定信號處理函數(shù),否則,設(shè)置該標(biāo)志變得毫無意義。即使為sa_sigaction指定了信號處理函數(shù),如果不設(shè)置SA_SIGINFO,信號處理函數(shù)同樣不能得到信號傳遞過來的數(shù)據(jù),在信號處理函數(shù)中對這些信息的訪問都將導(dǎo)致段錯誤(Segmentation fault)。

          注:很多文獻(xiàn)在闡述該標(biāo)志位時都認(rèn)為,如果設(shè)置了該標(biāo)志位,就必須定義三參數(shù)信號處理函數(shù)。實(shí)際不是這樣的,驗證方法很簡單:自己實(shí)現(xiàn)一個單一參數(shù)信號處理函數(shù),并在程序中設(shè)置該標(biāo)志位,可以察看程序的運(yùn)行結(jié)果。實(shí)際上,可以把該標(biāo)志位看成信號是否傳遞參數(shù)的開關(guān),如果設(shè)置該位,則傳遞參數(shù);否則,不傳遞參數(shù)。

          六、信號集及信號集操作函數(shù):

          信號集被定義為一種數(shù)據(jù)類型:

          
          	typedef struct {
          			unsigned long sig[_NSIG_WORDS];
          			} sigset_t
          

          信號集用來描述信號的集合,linux所支持的所有信號可以全部或部分的出現(xiàn)在信號集中,主要與信號阻塞相關(guān)函數(shù)配合使用。下面是為信號集操作定義的相關(guān)函數(shù):

          
          	#include <signal.h>
          int sigemptyset(sigset_t *set);
          int sigfillset(sigset_t *set);
          int sigaddset(sigset_t *set, int signum)
          int sigdelset(sigset_t *set, int signum);
          int sigismember(const sigset_t *set, int signum);
          
          sigemptyset(sigset_t *set)初始化由set指定的信號集,信號集里面的所有信號被清空;
          sigfillset(sigset_t *set)調(diào)用該函數(shù)后,set指向的信號集中將包含linux支持的64種信號;
          sigaddset(sigset_t *set, int signum)在set指向的信號集中加入signum信號;
          sigdelset(sigset_t *set, int signum)在set指向的信號集中刪除signum信號;
          sigismember(const sigset_t *set, int signum)判定信號signum是否在set指向的信號集中。
          

          七、信號阻塞與信號未決:

          每個進(jìn)程都有一個用來描述哪些信號遞送到進(jìn)程時將被阻塞的信號集,該信號集中的所有信號在遞送到進(jìn)程后都將被阻塞。下面是與信號阻塞相關(guān)的幾個函數(shù):

          
          #include <signal.h>
          int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));
          int sigpending(sigset_t *set));
          int sigsuspend(const sigset_t *mask));
          

          sigprocmask()函數(shù)能夠根據(jù)參數(shù)how來實(shí)現(xiàn)對信號集的操作,操作主要有三種:

          參數(shù)how 進(jìn)程當(dāng)前信號集
          SIG_BLOCK 在進(jìn)程當(dāng)前阻塞信號集中添加set指向信號集中的信號
          SIG_UNBLOCK 如果進(jìn)程阻塞信號集中包含set指向信號集中的信號,則解除對該信號的阻塞
          SIG_SETMASK 更新進(jìn)程阻塞信號集為set指向的信號集

          sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,卻被阻塞的所有信號,在set指向的信號集中返回結(jié)果。

          sigsuspend(const sigset_t *mask))用于在接收到某個信號之前, 臨時用mask替換進(jìn)程的信號掩碼, 并暫停進(jìn)程執(zhí)行,直到收到信號為止。sigsuspend 返回后將恢復(fù)調(diào)用之前的信號掩碼。信號處理函數(shù)完成后,進(jìn)程將繼續(xù)執(zhí)行。該系統(tǒng)調(diào)用始終返回-1,并將errno設(shè)置為EINTR。

          附錄1:結(jié)構(gòu)itimerval:

          
                      struct itimerval {
                          struct timeval it_interval; /* next value */
                          struct timeval it_value;    /* current value */
                      };
                      struct timeval {
                          long tv_sec;                /* seconds */
                          long tv_usec;               /* microseconds */
                      };
          

          附錄2:三參數(shù)信號處理函數(shù)中第二個參數(shù)的說明性描述:

          
          siginfo_t {
          int      si_signo;  /* 信號值,對所有信號有意義*/
          int      si_errno;  /* errno值,對所有信號有意義*/
          int      si_code;   /* 信號產(chǎn)生的原因,對所有信號有意義*/
          pid_t    si_pid;    /* 發(fā)送信號的進(jìn)程ID,對kill(2),實(shí)時信號以及SIGCHLD有意義 */
          uid_t    si_uid;    /* 發(fā)送信號進(jìn)程的真實(shí)用戶ID,對kill(2),實(shí)時信號以及SIGCHLD有意義 */
          int      si_status; /* 退出狀態(tài),對SIGCHLD有意義*/
          clock_t  si_utime;  /* 用戶消耗的時間,對SIGCHLD有意義 */
          clock_t  si_stime;  /* 內(nèi)核消耗的時間,對SIGCHLD有意義 */
          sigval_t si_value;  /* 信號值,對所有實(shí)時有意義,是一個聯(lián)合數(shù)據(jù)結(jié)構(gòu),可以為一個整數(shù)(由si_int標(biāo)示,也可以為一個指針,由si_ptr標(biāo)示)*/
          	
          void *   si_addr;   /* 觸發(fā)fault的內(nèi)存地址,對SIGILL,SIGFPE,SIGSEGV,SIGBUS 信號有意義*/
          int      si_band;   /* 對SIGPOLL信號有意義 */
          int      si_fd;     /* 對SIGPOLL信號有意義 */
          

          }

          實(shí)際上,除了前三個元素外,其他元素組織在一個聯(lián)合結(jié)構(gòu)中,在聯(lián)合數(shù)據(jù)結(jié)構(gòu)中,又根據(jù)不同的信號組織成不同的結(jié)構(gòu)。注釋中提到的對某種信號有意義指的是,在該信號的處理函數(shù)中可以訪問這些域來獲得與信號相關(guān)的有意義的信息,只不過特定信號只對特定信息感興趣而已。

          參考文獻(xiàn):

          1. linux內(nèi)核源代碼情景分析(上),毛德操、胡希明著,浙江大學(xué)出版社,當(dāng)要驗證某個結(jié)論、想法時,最好的參考資料;
          2. UNIX環(huán)境高級編程,作者:W.Richard Stevens,譯者:尤晉元等,機(jī)械工業(yè)出版社。對信號機(jī)制的發(fā)展過程闡述的比較詳細(xì)。
          3. signal、sigaction、kill等手冊,最直接而可靠的參考資料。
          4. http://www.linuxjournal.com/modules.php?op=modload&name=NS-help&file=man提供了許多系統(tǒng)調(diào)用、庫函數(shù)等的在線指南。
          5. http://www.opengroup.org/onlinepubs/007904975/可以在這里對許多關(guān)鍵函數(shù)(包括系統(tǒng)調(diào)用)進(jìn)行查詢,非常好的一個網(wǎng)址。
          6. http://unix.org/whitepapers/reentrant.html對函數(shù)可重入進(jìn)行了闡述。
          7. http://www.uccs.edu/~compsvcs/doc-cdrom/DOCS/HTML/APS33DTE/DOCU_006.HTM對實(shí)時信號給出了相當(dāng)好的描述。

          關(guān)于作者:

          鄭彥興,國防科大攻讀博士學(xué)位。聯(lián)系方式: mlinux@163.com

            轉(zhuǎn)自:http://www.ddvip.net/program/vc/index6/58.htm

          posted on 2005-08-04 13:00 weidagang2046 閱讀(170) 評論(0)  編輯  收藏 所屬分類: Linux

          主站蜘蛛池模板: 调兵山市| 蒙城县| 连云港市| 磴口县| 定结县| 鹰潭市| 清镇市| 保定市| 浠水县| 阜南县| 齐齐哈尔市| 盖州市| 高邑县| 东城区| 邮箱| 武功县| 昭通市| 雅安市| 泉州市| 长海县| 宣威市| 东台市| 新巴尔虎左旗| 修文县| 南昌市| 怀宁县| 类乌齐县| 合江县| 安泽县| 星子县| 马龙县| 廊坊市| 监利县| 留坝县| 巴里| 米易县| 镇宁| 阿瓦提县| 安徽省| 常熟市| 黄浦区|