成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux信號(signal) 機制分析(1)

系統 Linux
本文分析了Linux內核對于信號的實現機制和應用層的相關處理。首先介紹了軟中斷信號的本質及信號的兩種不同分類方法尤其是不可靠信號的原理。接著分析了內核對于信號的處理流程包括信號的觸發/注冊/執行及注銷等。最后介紹了應用層的相關處理,主要包括信號處理函數的安裝、信號的發送、屏蔽阻塞等,最后給了幾個簡單的應用實例。

【摘要】本文分析了Linux內核對于信號的實現機制和應用層的相關處理。首先介紹了軟中斷信號的本質及信號的兩種不同分類方法尤其是不可靠信號的原理。接著分析了內核對于信號的處理流程包括信號的觸發/注冊/執行及注銷等。***介紹了應用層的相關處理,主要包括信號處理函數的安裝、信號的發送、屏蔽阻塞等,***給了幾個簡單的應用實例。

【關鍵字】軟中斷信號,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t

[[181762]]

1. 信號本質

軟中斷信號(signal,又簡稱為信號)用來通知進程發生了異步事件。在軟件層次上是對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是進程間通信機制中唯一的異步通信機制,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什么時候到達。進程之間可以互相通過系統調用kill發送軟中斷信號。內核也可以因為內部事件而給進程發送信號,通知進程發生了某個事件。信號機制除了基本通知功能外,還可以傳遞附加信息。

收到信號的進程對各種信號有不同的處理方法。處理方法可以分為三類:

***種是類似中斷的處理程序,對于需要處理的信號,進程可以指定處理函數,由該函數來處理。

第二種方法是,忽略某個信號,對該信號不做任何處理,就象未發生過一樣。

第三種方法是,對該信號的處理保留系統的默認值,這種缺省操作,對大部分的信號的缺省操作是使得進程終止。進程通過系統調用signal來指定進程對某個信號的處理行為。

2. 信號的種類

可以從兩個不同的分類角度對信號進行分類:

  • 可靠性方面:可靠信號與不可靠信號;
  • 與時間的關系上:實時信號與非實時信號。

2.1 可靠信號與不可靠信號

Linux信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始,信號值小于SIGRTMIN的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是信號可能丟失。

隨著時間的發展,實踐證明了有必要對信號的原始機制加以改進和擴充。由于原來定義的信號已有許多應用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。

信號值位于SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝函數sigation()以及信號發送函數sigqueue()的同時,仍然支持早期的signal()信號安裝函數,支持信號發送函數kill()。

信號的可靠與不可靠只與信號值有關,與信號的發送及安裝函數無關。目前linux中的signal()是通過sigation()函數實現的,因此,即使通過signal()安裝的信號,在信號處理函數的結尾也不必再調用一次信號安裝函數。同時,由signal()安裝的實時信號支持排隊,同樣不會丟失。

對于目前linux的兩個信號安裝函數:signal()及sigaction()來說,它們都不能把SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊。這兩個函數的***區別在于,經過sigaction安裝的信號都能傳遞信息給信號處理函數,而經過signal安裝的信號不能向信號處理函數傳遞信息。對于信號發送函數來說也是一樣的。

2.2 實時信號與非實時信號

早期Unix系統只定義了32種信號,前32種信號已經有了預定義值,每個信號有了確定的用途及含義,并且每種信號都有各自的缺省動作。如按鍵盤的CTRL ^C時,會產生SIGINT信號,對該信號的默認反應就是進程終止。后32個信號表示實時信號,等同于前面闡述的可靠信號。這保證了發送的多個實時信號都被接收。

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

3. 信號處理流程

對于一個完整的信號生命周期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個階段:

  1. 信號誕生
  2. 信號在進程中注冊
  3. 信號的執行和注銷

3.1 信號誕生

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

這里按發出信號的原因簡單分類,以了解各種信號:

(1) 與進程終止相關的信號。當進程退出,或者子進程終止時,發出這類信號。

(2) 與進程例外事件相關的信號。如進程越界,或企圖寫一個只讀的內存區域(如程序正文區),或執行一個特權指令及其他各種硬件錯誤。

(3) 與在系統調用期間遇到不可恢復條件相關的信號。如執行系統調用exec時,原有資源已經釋放,而目前系統資源又已經耗盡。

(4) 與執行系統調用時遇到非預測錯誤條件相關的信號。如執行一個并不存在的系統調用。

(5) 在用戶態下的進程發出的信號。如進程調用系統調用kill向其他進程發送信號。

(6) 與終端交互相關的信號。如用戶關閉一個終端,或按下break鍵等情況。

(7) 跟蹤進程執行的信號。

Linux支持的信號列表如下。很多信號是與機器的體系結構相關的

信號值 默認處理動作 發出信號的原因

SIGHUP 1 A 終端掛起或者控制進程終止

SIGINT 2 A 鍵盤中斷(如break鍵被按下)

SIGQUIT 3 C 鍵盤的退出鍵被按下

SIGILL 4 C 非法指令

SIGABRT 6 C 由abort(3)發出的退出指令

SIGFPE 8 C 浮點異常

SIGKILL 9 AEF Kill信號

SIGSEGV 11 C 無效的內存引用

SIGPIPE 13 A 管道破裂: 寫一個沒有讀端口的管道

SIGALRM 14 A 由alarm(2)發出的信號

SIGTERM 1*** 終止信號

SIGUSR1 30,10,16 A 用戶自定義信號1

SIGUSR2 31,12,17 A 用戶自定義信號2

SIGCHLD 20,17,18 B 子進程結束信號

SIGCONT 19,18,25 進程繼續(曾被停止的進程)

SIGS***7,19,23 DEF 終止進程

SIGTSTP 18,20,24 D 控制終端(tty)上按下停止鍵

SIGTTIN 21,21,26 D 后臺進程企圖從控制終端讀

SIGTTOU 22,22,27 D 后臺進程企圖從控制終端寫

處理動作一項中的字母含義如下

A 缺省的動作是終止進程

B 缺省的動作是忽略此信號,將該信號丟棄,不做處理

C 缺省的動作是終止進程并進行內核映像轉儲(dump core),內核映像轉儲是指將進程數據在內存的映像和進程在內核結構中的部分內容以一定格式轉儲到文件系統,并且進程退出執行,這樣做的好處是為程序員提供了方便,使得他們可以得到進程當時執行時的數據值,允許他們確定轉儲的原因,并且可以調試他們的程序。

D 缺省的動作是停止進程,進入停止狀況以后還能重新進行下去,一般是在調試的過程中(例如ptrace系統調用)

E 信號不能被捕獲

F 信號不能被忽略

3.2 信號在目標進程中注冊

在進程表的表項中有一個軟中斷信號域,該域中每一位對應一個信號。內核給一個進程發送軟中斷信號的方法,是在進程所在的進程表項的信號域設置對應于該信號的位。如果信號發送給一個正在睡眠的進程,如果進程睡眠在可被中斷的優先級上,則喚醒進程;否則僅設置進程表中信號域相應的位,而不喚醒進程。如果發送給一個處于可運行狀態的進程,則只置相應的域即可。

進程的task_struct結構中有關于本進程中未決信號的數據成員: struct sigpending pending:

  1. struct sigpending{ 
  2.  
  3.         struct sigqueue *head, *tail; 
  4.  
  5.         sigset_t signal; 
  6.  
  7. };  

第三個成員是進程中所有未決信號集,***、第二個成員分別指向一個sigqueue類型的結構鏈(稱之為"未決信號信息鏈")的首尾,信息鏈中的每個sigqueue結構刻畫一個特定信號所攜帶的信息,并指向下一個sigqueue結構:

  1. struct sigqueue{ 
  2.  
  3.         struct sigqueue *next
  4.  
  5.         siginfo_t info; 
  6.  
  7.  

信號在進程中注冊指的就是信號值加入到進程的未決信號集sigset_t signal(每個信號占用一位)中,并且信號所攜帶的信息被保留到未決信號信息鏈的某個sigqueue結構中。只要信號在進程的未決信號集中,表明進程已經知道這些信號的存在,但還沒來得及處理,或者該信號被進程阻塞。

當一個實時信號發送給一個進程時,不管該信號是否已經在進程中注冊,都會被再注冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著同一個實時信號可以在同一個進程的未決信號信息鏈中占有多個sigqueue結構(進程每收到一個實時信號,都會為它分配一個結構來登記該信號信息,并把該結構添加在未決信號鏈尾,即所有誕生的實時信號都會在目標進程中注冊)。

當一個非實時信號發送給一個進程時,如果該信號已經在進程中注冊(通過sigset_t signal指示),則該信號將被丟棄,造成信號丟失。因此,非實時信號又叫做"不可靠信號"。這意味著同一個非實時信號在進程的未決信號信息鏈中,至多占有一個sigqueue結構。

總之信號注冊與否,與發送信號的函數(如kill()或sigqueue()等)以及信號安裝函數(signal()及sigaction())無關,只與信號值有關(信號值小于SIGRTMIN的信號最多只注冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信號,只要被進程接收到就被注冊)

3.3 信號的執行和注銷

內核處理一個進程收到的軟中斷信號是在該進程的上下文中,因此,進程必須處于運行狀態。當其由于被信號喚醒或者正常調度重新獲得CPU時,在其從內核空間返回到用戶空間時會檢測是否有信號等待處理。如果存在未決信號等待處理且該信號沒有被進程阻塞,則在運行相應的信號處理函數前,進程會把信號在未決信號鏈中占有的結構卸掉。

對于非實時信號來說,由于在未決信號信息鏈中最多只占用一個sigqueue結構,因此該結構被釋放后,應該把信號在進程未決信號集中刪除(信號注銷完畢);而對于實時信號來說,可能在未決信號信息鏈中占用多個sigqueue結構,因此應該針對占用sigqueue結構的數目區別對待:如果只占用一個sigqueue結構(進程只收到該信號一次),則執行完相應的處理函數后應該把信號在進程的未決信號集中刪除(信號注銷完畢)。否則待該信號的所有sigqueue處理完畢后再在進程的未決信號集中刪除該信號。

當所有未被屏蔽的信號都處理完畢后,即可返回用戶空間。對于被屏蔽的信號,當取消屏蔽后,在返回到用戶空間時會再次執行上述檢查處理的一套流程。

內核處理一個進程收到的信號的時機是在一個進程從內核態返回用戶態時。所以,當一個進程在內核態下運行時,軟中斷信號并不立即起作用,要等到將返回用戶態時才處理。進程只有處理完信號才會返回用戶態,進程在用戶態下不會有未處理完的信號。

處理信號有三種類型:進程接收到信號后退出;進程忽略該信號;進程收到信號后執行用戶設定用系統調用signal的函數。當進程接收到一個它忽略的信號時,進程丟棄該信號,就象沒有收到該信號似的繼續運行。如果進程收到一個要捕捉的信號,那么進程從內核態返回用戶態時執行用戶定義的函數。而且執行用戶定義的函數的方法很巧妙,內核是在用戶棧上創建一個新的層,該層中將返回地址的值設置成用戶定義的處理函數的地址,這樣進程從內核返回彈出棧頂時就返回到用戶定義的函數處,從函數返回再彈出棧頂時,才返回原先進入內核的地方。這樣做的原因是用戶定義的處理函數不能且不允許在內核態下執行(如果用戶定義的函數在內核態下運行的話,用戶就可以獲得任何權限)。

4. 信號的安裝

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

linux主要有兩個函數實現信號的安裝:signal()、sigaction()。其中signal()只有兩個參數,不支持信號傳遞信息,主要是用于前32種非實時信號的安裝;而sigaction()是較新的函數(由兩個系統調用實現:sys_signal以及sys_rt_sigaction),有三個參數,支持信號傳遞信息,主要用來與 sigqueue() 系統調用配合使用,當然,sigaction()同樣支持非實時信號的安裝。sigaction()優于signal()主要體現在支持信號帶有參數。

4.1 signal()

  1. #include <signal.h> 
  2.  
  3. void (*signal(int signum, void (*handler))(int)))(int);  

如果該函數原型不容易理解的話,可以參考下面的分解方式來理解:

  1. typedef void (*sighandler_t)(int); 
  2. sighandler_t signal(int signum, sighandler_t handler));  

***個參數指定信號的值,第二個參數指定針對前面信號值的處理,可以忽略該信號(參數設為SIG_IGN);可以采用系統默認方式處理信號(參數設為SIG_DFL);也可以自己實現處理方式(參數指定一個函數地址)。

如果signal()調用成功,返回***一次為安裝信號signum而調用signal()時的handler值;失敗則返回SIG_ERR。

傳遞給信號處理例程的整數參數是信號值,這樣可以使得一個信號處理例程處理多個信號。

  1. #include <signal.h> 
  2.  
  3. #include <unistd.h> 
  4.  
  5. #include <stdio.h> 
  6.  
  7. void sigroutine(int dunno) 
  8.  
  9. { /* 信號處理例程,其中dunno將會得到信號的值 */ 
  10.  
  11.         switch (dunno) { 
  12.  
  13.         case 1: 
  14.  
  15.         printf("Get a signal -- SIGHUP "); 
  16.  
  17.         break; 
  18.  
  19.         case 2: 
  20.  
  21.         printf("Get a signal -- SIGINT "); 
  22.  
  23.         break; 
  24.  
  25.         case 3: 
  26.  
  27.         printf("Get a signal -- SIGQUIT "); 
  28.  
  29.         break; 
  30.  
  31.         } 
  32.  
  33.         return
  34.  
  35.  
  36.   
  37.  
  38. int main() { 
  39.  
  40.         printf("process id is %d ",getpid()); 
  41.  
  42.         signal(SIGHUP, sigroutine); //* 下面設置三個信號的處理方法 
  43.  
  44.         signal(SIGINT, sigroutine); 
  45.  
  46.         signal(SIGQUIT, sigroutine); 
  47.  
  48.         for (;;) ; 
  49.  
  50.  

其中信號SIGINT由按下Ctrl-C發出,信號SIGQUIT由按下Ctrl-發出。該程序執行的結果如下:

  1. localhost:~$ ./sig_test 
  2.  
  3. process id is 463 
  4.  
  5. Get a signal -SIGINT //按下Ctrl-C得到的結果 
  6.  
  7. Get a signal -SIGQUIT //按下Ctrl-得到的結果 
  8.  
  9. //按下Ctrl-z將進程置于后臺 
  10.  
  11.  [1]+ Stopped ./sig_test 
  12.  
  13. localhost:~$ bg 
  14.  
  15.  [1]+ ./sig_test & 
  16.  
  17. localhost:~$ kill -HUP 463 //向進程發送SIGHUP信號 
  18.  
  19. localhost:~$ Get a signal – SIGHUP 
  20.  
  21. kill -9 463 //向進程發送SIGKILL信號,終止進程 
  22.  
  23. localhost:~$  

4.2 sigaction()

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

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

第二個參數最為重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數執行過程中應屏蔽掉哪些信號等等。

sigaction結構定義如下:

  1. struct sigaction { 
  2.  
  3.                        union
  4.  
  5.                                __sighandler_t _sa_handler; 
  6.  
  7.                                void (*_sa_sigaction)(int,struct siginfo *, void *); 
  8.  
  9.                        }_u 
  10.  
  11.             sigset_t sa_mask; 
  12.  
  13.             unsigned long sa_flags; 
  14.  
  15.  

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

2、由_sa_sigaction是指定的信號處理函數帶有三個參數,是為實時信號而設的(當然同樣支持非實時信號),它指定一個3參數信號處理函數。***個參數為信號值,第三個參數沒有使用,第二個參數是指向siginfo_t結構的指針,結構中包含信號攜帶的數據值,參數所指向的結構如下:

  1. siginfo_t { 
  2.  
  3.                   int      si_signo;  /* 信號值,對所有信號有意義*/ 
  4.  
  5.                   int      si_errno;  /* errno值,對所有信號有意義*/ 
  6.  
  7.                   int      si_code;   /* 信號產生的原因,對所有信號有意義*/ 
  8.  
  9.                                union{                               /* 聯合數據結構,不同成員適應不同信號 */ 
  10.  
  11.                                        //確保分配足夠大的存儲空間 
  12.  
  13.                                        int _pad[SI_PAD_SIZE]; 
  14.  
  15.                                        //對SIGKILL有意義的結構 
  16.  
  17.                                        struct{ 
  18.  
  19.                                                       ... 
  20.  
  21.                                                  }... 
  22.  
  23.                                                ... ... 
  24.  
  25.                                                ... ...                                
  26.  
  27.                                        //對SIGILL, SIGFPE, SIGSEGV, SIGBUS有意義的結構 
  28.  
  29.                                   struct{ 
  30.  
  31.                                                       ... 
  32.  
  33.                                                  }... 
  34.  
  35.                                                ... ... 
  36.  
  37.                                          } 
  38.  
  39.  

前面在討論系統調用sigqueue發送信號時,sigqueue的第三個參數就是sigval聯合數據結構,當調用sigqueue時,該數據結構中的數據就將拷貝到信號處理函數的第二個參數中。這樣,在發送信號同時,就可以讓信號傳遞一些附加信息。信號可以傳遞信息對程序開發是非常有意義的。

3、sa_mask指定在信號處理程序執行過程中,哪些信號應當被阻塞。缺省情況下當前信號本身被阻塞,防止信號的嵌套發送,除非指定SA_NODEFER或者SA_NOMASK標志位。

注:請注意sa_mask指定的信號阻塞的前提條件,是在由sigaction()安裝信號的處理函數執行過程中由sa_mask指定的信號才被阻塞。

4、sa_flags中包含了許多標志位,包括剛剛提到的SA_NODEFER及SA_NOMASK標志位。另一個比較重要的標志位是SA_SIGINFO,當設定了該標志位時,表示信號附帶的參數可以被傳遞到信號處理函數中,因此,應該為sigaction結構中的sa_sigaction指定處理函數,而不應該為sa_handler指定信號處理函數,否則,設置該標志變得毫無意義。即使為sa_sigaction指定了信號處理函數,如果不設置SA_SIGINFO,信號處理函數同樣不能得到信號傳遞過來的數據,在信號處理函數中對這些信息的訪問都將導致段錯誤(Segmentation fault)。 

責任編輯:龐桂玉 來源: 嵌入式Linux中文站
相關推薦

2017-01-16 15:05:17

Linux信號機制分析

2024-07-16 10:52:09

2025-06-25 06:18:46

Linux多線程機制

2017-08-16 16:20:01

Linux內核態搶占用戶態搶占

2011-06-09 09:45:35

Linux QT 信號

2011-07-05 18:32:52

QT 信號 機制

2011-06-23 13:38:27

QT 元對象 信號

2011-07-05 18:40:19

QT 信號 機制

2017-05-03 08:52:13

卷積神經網絡神經網絡非線性激活函數

2017-05-05 08:57:06

卷積神經網絡機制

2024-07-25 11:53:53

2011-06-23 14:40:13

Qt 信號

2021-12-10 00:01:53

Vsync信號機制

2025-02-03 07:00:00

2012-03-02 12:20:21

Javajmapjstack

2010-06-22 10:05:36

Linux監控

2021-11-11 17:40:08

WatchdogAndroid源碼分析

2014-05-22 14:57:28

Android消息處理機制Looper

2022-04-01 10:30:29

SignalLinux桌面Fedora

2009-04-20 21:20:32

Linux文件系統存儲機制
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 自拍偷拍精品 | 特黄色一级毛片 | 羞视频在线观看 | 日本在线你懂的 | 欧洲av在线 | 欧美电影在线观看网站 | 亚洲手机视频在线 | 欧美 中文字幕 | 国产精品日本一区二区不卡视频 | 欧美综合一区 | 91久久夜色精品国产网站 | 欧美日韩国产在线 | 久久久久久高潮国产精品视 | 一级毛片在线视频 | 99精品国产一区二区三区 | 99这里只有精品视频 | 国产精品不卡 | 亚洲国产欧美在线人成 | 一区二区中文 | 91xxx在线观看| 国产一区在线免费 | 五月天综合影院 | 欧美一区免费 | 久久久久久久久久久久久九 | 国产精品区一区二区三区 | 欧美日韩成人在线 | 亚洲激精日韩激精欧美精品 | 久久一| 欧美一区二区三区视频 | 免费高清av| 日本视频在线播放 | 日韩欧美一区二区三区在线播放 | 欧美自拍一区 | 日韩毛片 | 日韩成人免费视频 | 激情久久网 | 国产中文原创 | 97视频精品| 亚洲一区二区三区视频在线 | 性欧美精品一区二区三区在线播放 | 免费啪啪 |