linux信號的介紹


1.基本概念
    中斷:
          中斷是系統對於異步事件的響應
          中斷信號
          中斷源
          現場信息
          中斷處理程序
          中斷向量表
            異步事件的響應:進程執行代碼的過程中可以隨時被打斷,然后去執行異常處理程序
            生活中的中斷和計算機系統中的中斷
            1)    無中斷生活場景
            張三看書,廚房燒水
            2)有中斷的生活場景
            張三看書,設置鬧鍾,廚房燒水。
            鬧鍾發出中斷信號,張三把書合好(第20頁),去廚房把開水事情處理好,張三重新打開20頁進行閱讀。
            3)計算機系統的中斷場景
            中斷源發出中斷信號,CPU判斷中斷是否屏蔽屏蔽、保護現場,cpu執行中斷處理程序,cpu恢復現場,繼續原來的任務。
            4)中斷的其他概念
            中斷向量表保存了中斷處理程序的入口地址。
            中斷個數固定,操作系統啟動時初始化中斷向量表。
            中斷有優先級(有人敲門,有人打電話,有優先級)
            中斷可以屏蔽(張三可以屏蔽電話)。
    中斷分類:
              硬件中斷(外部中斷)
                 外部中斷是指由外部設備通過硬件請求的方式產生的中斷,也稱為硬件中斷
              軟件中斷(內部中斷)
                 內部中斷是由CPU運行程序錯誤或執行內部程序調用引起的一種中斷,也稱為軟件中斷。
              x86平台INT指令 ARM軟中斷指令SWI
    信號概念:
                   信號是UNIX系統響應某些狀況而產生的事件,進程在接收到信號時會采取相應的行動。
               信號是因為某些錯誤條件而產生的,比如內存段沖突、浮點處理器錯誤或者非法指令等
               信號是在軟件層次上對中斷的一種模擬,所以通常把它稱為是軟中斷
    信號和中斷的區別:
                   信號與中斷的相似點:
                         (1)采用了相同的異步通信方式;
                         (2)當檢測出有信號或中斷請求時,都暫停正在執行的程序而轉去執行相應的處理程序;
                         (3)都在處理完畢后返回到原來的斷點;
                         (4)對信號或中斷都可進行屏蔽。
                    信號與中斷的區別:
                       (1)中斷有優先級,而信號沒有優先級,所有的信號都是平等的;
                       (2)信號處理程序是在用戶態下運行的,而中斷處理程序是在核心態下運行;
                       (3)中斷響應是及時的,而信號響應通常都有較大的時間延遲。
2.信號名稱及常用信號解釋
    kill –l    可以查看linux內核支持的信號
    Man 7 signal 查看信號的默認動作、信號的含義
            信號名稱    描述
            SIGABRT        進程停止運行 6
            SIGALRM        警告鍾
            SIGFPE        算述運算例外
            SIGHUP        系統掛斷
            SIGILL        非法指令
            SIGINT        終端中斷 2
            SIGKILL        停止進程(此信號不能被忽略或捕獲)
            SIGPIPE        向沒有讀者的管道寫入數據
            SIGSEGV        無效內存段訪問
            SIGQUIT        終端退出3
            SIGTERM        終止
            SIGUSR1        用戶定義信號1
            SIGUSR2        用戶定義信號2
            SIGCHLD        子進程已經停止或退出
            SIGCONT        如果被停止則繼續執行
            SIGSTOP        停止執行
            SIGTSTP        終端停止信號
            SIGTOUT        后台進程請求進行寫操作
            SIGTTIN        后台進程請求進行讀操作
3.信號處理
    進程對信號的三種響應
          1)忽略信號
                不采取任何操作、有兩個信號不能被忽略:SIGKILL(9號信號)和SIGSTOP。
                注意:為什么進程不能忽略SIGKILL、SIGSTOP信號。(如果應用程序可以忽略這2個信號,系統管理無法殺死、暫停進程,無法對系統進行管理。)。SIGKILL(9號信號)和SIGSTOP信號是不能被捕獲的。
          2)捕獲並處理信號
                內核中斷正在執行的代碼,轉去執行先前注冊過的處理程序。
          3)執行默認操作
                默認操作通常是終止進程,這取決於被發送的信號。
                注意:信號的默認操作:通過 man 7 signal 進程查看
4.signal信號安裝函數
    1)signal函數作用
                (1):站在應用程序的角度,注冊一個信號處理函數。
                (2):忽略信號、設置信號默認處理信號的安裝和恢復
                        typedef void (*__sighandler_t) (int);
                        #define SIG_ERR ((__sighandler_t) -1)
                        #define SIG_DFL ((__sighandler_t) 0)
                        #define SIG_IGN ((__sighandler_t) 1)
    2)函數原型
        __sighandler_t signal(intsignum, __sighandler_t handler);
        參數:
                1)signal是一個帶signum和handler兩個參數的函數,准備捕捉或屏蔽的信號由參數signum給出,接收到指定信號時將要調用的函數由handler給出
            2)handler這個函數必須有一個int類型的參數(即接收到的信號代碼),它本身的類型是void
            3)handler也可以是下面兩個特殊值:
                                                SIG_IGN    屏蔽該信號
                                                SIG_DFL    恢復默認行為
5.信號發送
    1)kill函數
            kill基本用法:    
                        發送信號的函數有kill和raise
                        區別:kill既可以向自身發送信號,也可以向其他進程發送信號;
                        raise函數向進程自身發送信號。
            intkill(pid_tpid, intsiq)
            int raise(intsigno)
            參數組合情況解釋:
                            kill(pid_tpid, intsiq)
                            pid>0 將信號sig發給pid進程
                            pid=0 將信號sig發給同組進程
                            pid=-1 將信號sig發送給所有進程,調用者進程有權限發送的每一個進程(除了1號進程之外,還有它自身)
                            pid<-1 將信號sig發送給進程組是pid(絕對值)的每一個進程    

                        注意:如果在fork之前安裝信號,則子進程可以繼承信號。
            sleep函數幾點說明:
                            (1)sleep函數作用,讓進程睡眠。
                            (2)能被信號打斷,然后處理信號函數以后,就不再睡眠了。直接向下執行代碼
                            (3)sleep函數的返回值,是剩余的秒數
    2)raise函數
                
            raise給自己發送信號。raise(sig)等價於kill(getpid(), sig);
            killpg給進程組發送信號。killpg(pgrp, sig)等價於kill(-pgrp, sig);
            sigqueue給進程發送信號,支持排隊,可以附帶額外數據信息。
    3)alarm函數
            unsigned int alarm(unsigned int seconds);
            alarm函數,設置一個鬧鍾延遲發送信號告訴linux內核n秒中以后,發送SIGALRM信號

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include <signal.h>
 4 #include <unistd.h>
 5 #include<errno.h>
 6 void  myhandle(int num)
 7 {
 8     printf("recv signal id num : %d \n", num);
 9     alarm(1);
10 }
11 int main()
12 {
13     printf("main ....begin\n");
14     if (signal(SIGALRM, myhandle) == SIG_ERR)
15     {
16         perror("func signal err\n");
17         return 0;
18     }
19     alarm(1);
20     while(1)
21     {
22         pause();
23         printf("pause return\n");
24     }
25     return 0;
26 }

6.信號的阻塞和未達
        信號在內核中的表示:
            執行信號的處理動作稱為信號遞達(Delivery),信號從產生到遞達之間的狀態,稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。
           注意:阻塞和忽略是不同,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作。信號在內核中的表示可以看作是這樣的:

      

       說明1)PCB進程控制塊中結構體中有信號屏蔽狀態字(block),信號未決狀態字(pending)還有是否忽略標志;
            說明2)信號屏蔽狀態字(block),1代表阻塞、0代表不阻塞;信號未決狀態字(pending)的1代表未決(表示有未達信號),0代表信號可以抵達了;
            說明3)向進程發送SIGINT,內核首先判斷信號屏蔽狀態字是否阻塞,信號未決狀態字(pending相應位制成1;若阻塞解除,信號未決狀態字(pending)相應位制成0;表示信號可以抵達了。
            說明4)block狀態字、pending狀態字 64bit;//socket select
            說明5)block狀態字用戶可以讀寫,pending狀態字用戶只能讀;這是信號設計機制。

   信號集操作函數(狀態字表示)
                intsigemptyset(sigset_t *set);把信號集清空64bit/8=8個字節
                intsigfillset(sigset_t *set);把信號集置成1
                intsigaddset(sigset_t *set, intsigno);根據signo,把信號集中的對應bit置成1
                intsigdelset(sigset_t *set, intsigno);根據signo,把信號集中的對應bit置成0
                intsigismember(constsigset_t *set, intsigno);//判斷signo是否在信號集中
        sigprocmask讀取或更改進程的信號屏蔽狀態字(block)
                int sigprocmask(int how, constsigset_t *set, sigset_t *oset);
                返回值:若成功則為0,若出錯則為-1
                如果oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。如果set是非空指針,則更改進程的信號屏蔽字,參數how指示如何更改。如果oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset里,然后根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字為mask,下表說明了how參數的可選值。

      

      SIG_BLOCK ,講信號集set添加到進程block狀態字中。

    sigpending獲取信號未決狀態字(pending)信息:int   sigpending(sigset_t *set);

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include <signal.h>
 4 #include <unistd.h>
 5 #include<errno.h>
 6 void handler(int sig)
 7 {
 8     if (sig == SIGINT)
 9         printf("recv a sig=%d\n", sig);
10     else if (sig == SIGQUIT)
11     {
12         sigset_t uset;
13         sigemptyset(&uset);
14         sigaddset(&uset, SIGINT);
15         //ctr + \ 用來觸發  SIGINT 信號
16         //解除阻塞
17         sigprocmask(SIG_UNBLOCK, &uset, NULL);
18     }
19 }
20 
21 void printsigset(sigset_t *set)
22 {
23     int i;
24     for (i=1; i<NSIG; ++i)
25     {
26         if (sigismember(set, i))
27             putchar('1');
28         else
29             putchar('0');
30     }
31     printf("\n");
32 }
33 // 連續的按ctrl+c鍵盤,雖然發送了多個SIGINT信號,但是因為信號是不穩定的,只保留了一個。
34 //不支持排隊
35 int main(int argc, char *argv[])
36 {
37     sigset_t pset; //用來打印的信號集
38     sigset_t bset; //用來設置阻塞的信號集
39     
40     sigemptyset(&bset);
41     sigaddset(&bset, SIGINT);
42     
43     if (signal(SIGINT, handler) == SIG_ERR)
44         ERR_EXIT("signal error");
45         
46     if (signal(SIGQUIT, handler) == SIG_ERR)
47         ERR_EXIT("signal error");
48 
49     //讀取或更改進程的信號屏蔽字這里用來阻塞ctrl+c信號
50     //ctrl+c信號被設置成阻塞,即使用戶按下ctl+c鍵盤,也不會抵達
51     sigprocmask(SIG_BLOCK, &bset, NULL);
52     
53     for (;;)
54     {
55         //獲取未決字信息
56         sigpending(&pset);
57         
58         //打印信號未決sigset_t字
59         printsigset(&pset);
60         sleep(1);
61     }
62     return 0;
63 }

7.sigaction函數
        包含頭文件<signal.h>
        功能:sigaction函數用於改變進程接收到特定信號后的行為。
        原型:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
        參數:
                該函數的第一個參數為信號的值,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號(為這兩個信號定義自己的處理函數,將導致信號安裝錯誤)
                第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對特定信號的處理,可以為空,進程會以缺省方式對信號處理
                第三個參數oldact指向的對象用來保存原來對相應信號的處理,可指定oldact為NULL。
        返回值:函數成功返回0,失敗返回-1
        sigaction結構體:
                第二個參數最為重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數執行過程中應屏蔽掉哪些函數等等
                struct sigaction {
                    void (*sa_handler)(int);   //信號處理程序不接受額外數據
                    void (*sa_sigaction)(int, siginfo_t *, void *); //信號處理程序能接受額外數據,和sigqueue配合使用
                    sigset_t sa_mask; //
                    int sa_flags; //影響信號的行為SA_SIGINFO表示能接受數據
                    void (*sa_restorer)(void); //廢棄
                    };
                    注意:回調函數句柄sa_handler、sa_sigaction只能任選其一。

   sigaction的函數注冊信號,基本用法代碼:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include <signal.h>
 4 #include <unistd.h>
 5 #include<errno.h>
 6 void handler(int sig)
 7 {
 8     printf("recv a sig=%d\n", sig);    
 9 }
10 
11 __sighandler_t my_signal(int sig, __sighandler_t handler)
12 {
13     struct sigaction act;
14     struct sigaction oldact;
15     act.sa_handler = handler;
16     sigemptyset(&act.sa_mask);
17     act.sa_flags = 0;
18 
19     if (sigaction(sig, &act, &oldact) < 0)
20         return SIG_ERR;
21 
22     return oldact.sa_handler;
23 }
24 
25 int main(int argc, char *argv[])
26 {         
27     struct sigaction act;
28     sigset_t sa_mask;
29     
30     act.sa_handler = handler;
31     act.sa_flags = 0;
32     sigemptyset(&act.sa_mask);
33 
34     //測試信號安裝函數
35     sigaction(SIGINT, &act, NULL);
36     
37     //模擬signal函數
38     //my_signal(SIGINT, handler);
39 
40     for (;;)
41     {
42         pause();
43     }
44     return 0;
45 }

  如上圖修改對應地方編譯后,都可以實現效果

  測試sigaction結構體第三個參數sigset_tsa_mask的作用,代碼如下:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include <signal.h>
 4 #include <unistd.h>
 5 #include<errno.h>
 6 /*
 7 struct sigaction {
 8     void (*sa_handler)(int);
 9     void (*sa_sigaction)(int, siginfo_t *, void *);
10     sigset_t sa_mask;
11     int sa_flags;
12     void (*sa_restorer)(void);
13   } */
14 //測試sigaction結構體第三個參數sigset_t sa_mask的作用
15 //作用sigaddset(&act.sa_mask, SIGQUIT); 加入到sa_mask中的信號,被阻塞(信號處理函數執行的過程中被阻塞)。
16 //注意:SIGQUIT信號最終還會抵達
17 void handler(int sig);
18 int main(int argc, char *argv[])
19 {
20     struct sigaction act;
21     act.sa_handler = handler;
22     
23     sigemptyset(&act.sa_mask);
24     sigaddset(&act.sa_mask, SIGQUIT);
25     act.sa_flags = 0;
26 
27     if (sigaction(SIGINT, &act, NULL) < 0)
28     {
29         perror("sigaction error");
30     }
31 
32     for (;;)
33     {
34         pause();
35     }
36     return 0;
37 }
38 
39 void handler(int sig)
40 {
41     printf("recv a sig=%d 信號處理函數執行的時候,阻塞sa_mask中的信號\n", sig);
42     sleep(5);
43 }

結論:sigaddset(&act.sa_mask, SIGQUIT) 加入到sa_mask中的信號,被阻塞(信號處理函數執行的過程中被阻塞),SIGQUIT信號最終還會抵達;

8.sigqueue函數
        功能:新的發送信號系統調用,主要是針對實時信號提出的支持信號帶有參數,與函數sigaction()配合使用。
     注意:和kill函數相比int kill(pid_tpid, intsiq)多了參數
     原型:int sigqueue(pid_t pid, int sig, const union sigval value);
        參數:
           sigqueue的第1個參數是指定接收信號的進程id,第2個參數確定即將發送的信號,第3個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。
                        union sigval {
                                int   sival_int;
                                void *sival_ptr;
                                };
        返回值:成功返回0,失敗返回-1
        sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。
9.其他介紹
    三種睡眠函數:
            unsigned int sleep(unsigned int seconds);秒
            若被中斷打斷,返回剩余時間
            int usleep(useconds_tusec);微妙
            若被中斷打斷,返回剩余時間
            int nanosleep(const struct timespec *req, struct timespec *rem);納秒時間
            要睡眠的時間req;剩余睡眠時間,如果要中斷,通過rem返回過來。
    三種時間結構:
            time_t秒
            struct timeval {
                long    tv_sec;         /* seconds */
                long    tv_usec;        /* microseconds */微妙
            };
            struct timespec {納秒
                time_ttv_sec;        /* seconds */
                long   tv_nsec;       /* nanoseconds */
            };
    setitime函數:間隙性產生
            包含頭文件<sys/time.h>
            功能setitimer()比alarm功能強大,支持3種類型的定時器
            原型:int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
            參數:
                第一個參數which指定定時器類型
                第二個參數是結構itimerval的一個實例,結構itimerval形式
                第三個參數可不做處理。
            返回值:成功返回0失敗返回-1
            ITIMER_REAL:經過指定的時間后,內核將發送SIGALRM信號給本進程
            ITIMER_VIRTUAL :程序在用戶空間執行指定的時間后,內核將發送SIGVTALRM信號給本進程
            ITIMER_PROF :進程在內核空間中執行時,時間計數會減少,通常與ITIMER_VIRTUAL共用,代表進程在用戶空間與內核空間中運行指定時間后,內核將發送SIGPROF信號給本進程.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM