• <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>
            xiaoguozi's Blog
            Pay it forword - 我并不覺的自豪,我所嘗試的事情都失敗了······習(xí)慣原本生活的人不容易改變,就算現(xiàn)狀很糟,他們也很難改變,在過程中,他們還是放棄了······他們一放棄,大家就都是輸家······讓愛傳出去,很困難,也無法預(yù)料,人們需要更細(xì)心的觀察別人,要隨時注意才能保護(hù)別人,因為他們未必知道自己要什么·····

            一 信號的種類

            可靠信號與不可靠信號, 實時信號與非實時信號

            可靠信號就是實時信號, 那些從UNIX系統(tǒng)繼承過來的信號都是非可靠信號, 表現(xiàn)在信號

            不支持排隊,信號可能會丟失, 比如發(fā)送多次相同的信號, 進(jìn)程只能收到一次. 信號值小于

            SIGRTMIN的都是非可靠信號.

            非可靠信號就是非實時信號, 后來, Linux改進(jìn)了信號機制, 增加了32種新的信號, 這些信

            號都是可靠信號, 表現(xiàn)在信號支持排隊, 不會丟失, 發(fā)多少次, 就可以收到多少次. 信號值

            位于 [SIGRTMIN, SIGRTMAX] 區(qū)間的都是可靠信號.

             

            關(guān)于可靠信號, 還可以參考WIKI的一段話:

             

            Text代碼  收藏代碼
            1. The real-time signals, ranging from SIGRTMIN to SIGRTMAX, are a set of signals that can be used for application-defined purposes.  
            2. Because SIGRTMIN may have different values on different Unix-like systems, applications should always refer to the signals in the form SIGRTMIN+n, where n is a constant integer expression.  
            3. The real-time signals have a number of properties that differentiate them from other signals and make them suitable for application-defined purposes:  
            4. * Multiple instances of a real-time signal can be sent to a process and all will be delivered.  
            5. * Real-time signals can be accompanied by an integer or pointer value (see sigqueue[2]).  
            6. * Real-time signals are guaranteed to be delivered in the order they were emitted.  
             


            命令行輸入 kill -l, 可以列出系統(tǒng)支持的所有信號:

             

            C代碼  收藏代碼
            1. ~> kill -l  
            2.  1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP  
            3.  6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1  
            4. 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM  
            5. 16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP  
            6. 21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ  
            7. 26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR  
            8. 31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  
            9. 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8  
            10. 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13  
            11. 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12  
            12. 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  
            13. 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2  
            14. 63) SIGRTMAX-1  64) SIGRTMAX      

            非可靠信號一般都有確定的用途及含義,  可靠信號則可以讓用戶自定義使用

             

            二 信號的安裝

             

            早期的Linux使用系統(tǒng)調(diào)用 signal 來安裝信號


            #include <signal.h>

            void (*signal(int signum, void (*handler))(int)))(int); 

            該函數(shù)有兩個參數(shù), signum指定要安裝的信號, handler指定信號的處理函數(shù).

            該函數(shù)的返回值是一個函數(shù)指針, 指向上次安裝的handler


            經(jīng)典安裝方式:

            if (signal(SIGINT, SIG_IGN) != SIG_IGN) {

                signal(SIGINT, sig_handler);

            }

            先獲得上次的handler, 如果不是忽略信號, 就安裝此信號的handler


            由于信號被交付后, 系統(tǒng)自動的重置handler為默認(rèn)動作, 為了使信號在handler

            處理期間, 仍能對后繼信號做出反應(yīng), 往往在handler的第一條語句再次調(diào)用 signal


            sig_handler(ing signum)

            {

                /* 重新安裝信號 */

                signal(signum, sig_handler);

                ......

            }

            我們知道在程序的任意執(zhí)行點上, 信號隨時可能發(fā)生, 如果信號在sig_handler重新安裝

            信號之前產(chǎn)生, 這次信號就會執(zhí)行默認(rèn)動作, 而不是sig_handler. 這種問題是不可預(yù)料的.


            使用庫函數(shù) sigaction  來安裝信號

            為了克服非可靠信號并同一SVR4和BSD之間的差異, 產(chǎn)生了 POSIX 信號安裝方式, 使用

            sigaction安裝信號的動作后, 該動作就一直保持, 直到另一次調(diào)用 sigaction建立另一個

            動作為止. 這就克服了古老的 signal 調(diào)用存在的問題


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


            經(jīng)典安裝方式:


            struct sigaction action, old_action;



            /* 設(shè)置SIGINT */

            action.sa_handler = sig_handler;

            sigemptyset(&action.sa_mask);

            sigaddset(&action.sa_mask, SIGTERM);

            action.sa_flags = 0;


            /* 獲取上次的handler, 如果不是忽略動作, 則安裝信號 */

            sigaction(SIGINT, NULL, &old_action);

            if (old_action.sa_handler != SIG_IGN) {

                sigaction(SIGINT, &action, NULL);

            }


            基于 sigaction 實現(xiàn)的庫函數(shù): signal

            sigaction 自然強大, 但安裝信號很繁瑣, 目前l(fā)inux中的signal()是通過sigation()函數(shù)

            實現(xiàn)的,因此,即使通過signal()安裝的信號,在信號處理函數(shù)的結(jié)尾也不必

            再調(diào)用一次信號安裝函數(shù)。

             

            三 如何屏蔽信號

             

            所謂屏蔽, 并不是禁止遞送信號, 而是暫時阻塞信號的遞送, 

            解除屏蔽后, 信號將被遞送, 不會丟失. 相關(guān)API為


            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);

            int sigsuspend(const sigset_t *mask);

            int sigpending(sigset_t *set);

            -----------------------------------------------------------------

            int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));

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

            * SIG_BLOCK 在進(jìn)程當(dāng)前阻塞信號集中添加set指向信號集中的信號

            * SIG_UNBLOCK 如果進(jìn)程阻塞信號集中包含set指向信號集中的信號,則解除

               對該信號的阻塞

            * SIG_SETMASK 更新進(jìn)程阻塞信號集為set指向的信號集


            屏蔽整個進(jìn)程的信號:

             

            C代碼  收藏代碼
            #include <signal.h>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <error.h>
            #include 
            <string.h>

            void sig_handler(int signum)
            {
                printf(
            "catch SIGINT\n");
            }

            int main(int argc, char **argv)
            {
                sigset_t block;
                
            struct sigaction action, old_action;

                
            /* 安裝信號 */
                action.sa_handler 
            = sig_handler;
                sigemptyset(
            &action.sa_mask);
                action.sa_flags 
            = 0;

                sigaction(SIGINT, NULL, 
            &old_action);
                
            if (old_action.sa_handler != SIG_IGN) {
                    sigaction(SIGINT, 
            &action, NULL);
                }

                
            /* 屏蔽信號 */
                sigemptyset(
            &block);
                sigaddset(
            &block, SIGINT);

                printf(
            "block SIGINT\n");
                sigprocmask(SIG_BLOCK, 
            &block, NULL);

                printf(
            "--> send SIGINT -->\n");
                kill(getpid(), SIGINT);
                printf(
            "--> send SIGINT -->\n");
                kill(getpid(), SIGINT);
                sleep(
            1);

                
            /* 解除信號后, 之前觸發(fā)的信號將被遞送,
                 * 但SIGINT是非可靠信號, 只會遞送一次
                 
            */
                printf(
            "unblock SIGINT\n");
                sigprocmask(SIG_UNBLOCK, 
            &block, NULL);

                sleep(
            2);

                
            return 0;
            }

             運行結(jié)果:

             

            C代碼  收藏代碼
            work> ./a.out   
            block SIGINT  
            --> send SIGINT -->  
            --> send SIGINT -->  
            unblock SIGINT  
            catch SIGINT

            這里發(fā)送了兩次SIGINT信號 可以看到, 屏蔽掉SIGINT后,

            信號無法遞送, 解除屏蔽后, 才遞送信號, 但只被遞送一次,

            因為SIGINT是非可靠信號, 不支持排隊.


            只在信號處理期間, 屏蔽其它信號

            在信號的handler執(zhí)行期間, 系統(tǒng)將自動屏蔽此信號, 但如果

            還想屏蔽其它信號怎么辦?  可以利用 struct sigaction 結(jié)構(gòu)體

            的 sa_mask 屬性.

             

            C代碼  收藏代碼
            #include <signal.h>  
            #include 
            <stdio.h>  
            #include 
            <stdlib.h>  
            #include 
            <error.h>  
            #include 
            <string.h>  
              
            void sig_handler(int signum)  
            {  
                printf(
            "in handle, SIGTERM is blocked\n");  
                
            /* 在此handler內(nèi)將屏蔽掉SIGTERM, 直到此handler返回 */  
                printf(
            "--> send SIGTERM -->\n");  
                kill(getpid(), SIGTERM);  
                sleep(
            5);  
                printf(
            "handle done\n");  
            }  
              
            void handle_term(int signum)  
            {  
                printf(
            "catch sigterm and exit..\n");  
                exit(
            0);  
            }  
              
            int main(int argc, char **argv)  
            {  
                
            struct sigaction action, old_action;  
              
                
            /* 設(shè)置SIGINT */  
                action.sa_handler 
            = sig_handler;  
                sigemptyset(
            &action.sa_mask);  
                
            /* 安裝handler的時候, 設(shè)置在handler 
                 * 執(zhí)行期間, 屏蔽掉SIGTERM信號 
            */  
                sigaddset(
            &action.sa_mask, SIGTERM);  
                action.sa_flags 
            = 0;  
              
                sigaction(SIGINT, NULL, 
            &old_action);  
                
            if (old_action.sa_handler != SIG_IGN) {  
                    sigaction(SIGINT, 
            &action, NULL);  
                }  
              
                
            /* 設(shè)置SIGTERM */  
                action.sa_handler 
            = handle_term;  
                sigemptyset(
            &action.sa_mask);  
                action.sa_flags 
            = 0;  
              
                sigaction(SIGTERM, NULL, 
            &old_action);  
                
            if (old_action.sa_handler != SIG_IGN) {  
                    sigaction(SIGTERM, 
            &action, NULL);  
                }  
              
                printf(
            "--> send SIGINT -->\n");  
                kill(getpid(), SIGINT);  
              
                
            while (1) {  
                    sleep(
            1);  
                }  
              
                
            return 0;  
            }

             

            運行結(jié)果:

             

            C代碼  收藏代碼
            work> ./a.out
            --> send SIGINT -->
            in handle, SIGTERM is blocked
            --> send SIGTERM -->
            handle done
            catch sigterm and exit..

             收到SIGINT后, 進(jìn)入sig_handler,此時發(fā)送SIGTERM信號將被屏蔽,

             等sig_handler返回后, 才收到SIGTERM信號, 然后退出程序

             

             

            四 如何獲取未決信號

            所謂未決信號, 是指被阻塞的信號, 等待被遞送的信號. 


            int sigsuspend(const sigset_t *mask));

            sigpending(sigset_t *set))獲得當(dāng)前已遞送到進(jìn)程,

            卻被阻塞的所有信號,在set指向的信號集中返回結(jié)果。

             

            C代碼  收藏代碼
            #include <signal.h>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <error.h>
            #include 
            <string.h>

            /* 版本1, 可靠信號將被遞送多次 */
            //#define MYSIGNAL SIGRTMIN+5
            /*
             版本2, 不可靠信號只被遞送一次 */
            #define MYSIGNAL SIGTERM

            void sig_handler(int signum)
            {
                psignal(signum, 
            "catch a signal");
            }

            int main(int argc, char **argv)
            {
                sigset_t block, pending;
                
            int sig, flag;

                
            /* 設(shè)置信號的handler */
                signal(MYSIGNAL, sig_handler);

                
            /* 屏蔽此信號 */
                sigemptyset(
            &block);
                sigaddset(
            &block, MYSIGNAL);
                printf(
            "block signal\n");
                sigprocmask(SIG_BLOCK, 
            &block, NULL);

                
            /* 發(fā)兩次信號, 看信號將會被觸發(fā)多少次 */
                printf(
            "---> send a signal --->\n");
                kill(getpid(), MYSIGNAL);
                printf(
            "---> send a signal --->\n");
                kill(getpid(), MYSIGNAL);

                
            /* 檢查當(dāng)前的未決信號 */
                flag 
            = 0;
                sigpending(
            &pending);
                
            for (sig = 1; sig < NSIG; sig++) {
                    
            if (sigismember(&pending, sig)) {
                        flag 
            = 1;
                        psignal(sig, 
            "this signal is pending");
                    } 
                }
                
            if (flag == 0) {
                    printf(
            "no pending signal\n");
                }

                
            /* 解除此信號的屏蔽, 未決信號將被遞送 */
                printf(
            "unblock signal\n");
                sigprocmask(SIG_UNBLOCK, 
            &block, NULL);

                
            /* 再次檢查未決信號 */
                flag 
            = 0;
                sigpending(
            &pending);
                
            for (sig = 1; sig < NSIG; sig++) {
                    
            if (sigismember(&pending, sig)) {
                        flag 
            = 1;
                        psignal(sig, 
            "a pending signal");
                    } 
                }
                
            if (flag == 0) {
                    printf(
            "no pending signal\n");
                }

                
            return 0;
            }

              這個程序有兩個版本:

            可靠信號版本, 運行結(jié)果:

            C代碼  收藏代碼
            work> ./a.out   
            block signal  
            ---> send a signal --->  
            ---> send a signal --->  
            this signal is pending: Unknown signal 39  
            unblock signal  
            catch a signal: Unknown signal 39  
            catch a signal: Unknown signal 39  
            no pending signal

            發(fā)送兩次可靠信號, 最終收到兩次信號


            非可靠信號版本, 運行結(jié)果:

             

            C代碼  收藏代碼
            work> ./a.out   
            block signal  
            ---> send a signal --->  
            ---> send a signal --->  
            this signal is pending: Terminated  
            unblock signal  
            catch a signal: Terminated  
            no pending signal

              發(fā)送兩次非可靠信號, 最終只收到一次

             

            五 被中斷了的系統(tǒng)調(diào)用

            一些IO系統(tǒng)調(diào)用執(zhí)行時, 如 read 等待輸入期間, 如果收到一個信號,

            系統(tǒng)將中斷read, 轉(zhuǎn)而執(zhí)行信號處理函數(shù). 當(dāng)信號處理返回后, 系統(tǒng)

            遇到了一個問題: 是重新開始這個系統(tǒng)調(diào)用, 還是讓系統(tǒng)調(diào)用失敗?


            早期UNIX系統(tǒng)的做法是, 中斷系統(tǒng)調(diào)用, 并讓系統(tǒng)調(diào)用失敗, 比如read

            返回 -1, 同時設(shè)置 errno 為 EINTR


            中斷了的系統(tǒng)調(diào)用是沒有完成的調(diào)用, 它的失敗是臨時性的, 如果再次調(diào)用

            則可能成功, 這并不是真正的失敗, 所以要對這種情況進(jìn)行處理, 典型的方式為:


            while (1) {

                n = read(fd, buf, BUFSIZ);

                if (n == -1 && errno != EINTR) {

                    printf("read error\n");

                    break;

                }

                if (n == 0) {

                    printf("read done\n");

                    break;

                }

            }


            這樣做邏輯比較繁瑣, 事實上, 我們可以從信號的角度

            來解決這個問題,  安裝信號的時候, 設(shè)置 SA_RESTART

            屬性, 那么當(dāng)信號處理函數(shù)返回后, 被該信號中斷的系統(tǒng)

            調(diào)用將自動恢復(fù). 

             

            C代碼  收藏代碼

                #include <signal.h>  
                #include 
            <stdio.h>  
                #include 
            <stdlib.h>  
                #include 
            <error.h>  
                #include 
            <string.h>  
                  
                
            void sig_handler(int signum)  
                {  
                    printf(
            "in handler\n");  
                    sleep(
            1);  
                    printf(
            "handler return\n");  
                }  
                  
                
            int main(int argc, char **argv)  
                {  
                    
            char buf[100];  
                    
            int ret;  
                    
            struct sigaction action, old_action;  
                  
                    action.sa_handler 
            = sig_handler;  
                    sigemptyset(
            &action.sa_mask);  
                    action.sa_flags 
            = 0;  
                    
            /* 版本1:不設(shè)置SA_RESTART屬性 
                     * 版本2:設(shè)置SA_RESTART屬性 
            */  
                    
            //action.sa_flags |= SA_RESTART;  
                  
                    sigaction(SIGINT, NULL, 
            &old_action);  
                    
            if (old_action.sa_handler != SIG_IGN) {  
                        sigaction(SIGINT, 
            &action, NULL);  
                    }  
                  
                    bzero(buf, 
            100);  
                  
                    ret 
            = read(0, buf, 100);  
                    
            if (ret == -1) {  
                        perror(
            "read");  
                    }  
                  
                    printf(
            "read %d bytes:\n", ret);  
                    printf(
            "%s\n", buf);  
                  
                    
            return 0;  
                } 

             

            版本1, 不設(shè)置 SA_RESTART 屬性 :

             

            C代碼  收藏代碼
            work> gcc signal.c   
            work
            > ./a.out   
            ^Cin handler  
            handler 
            return  
            read: Interrupted system call  
            read 
            -1 bytes:

            在 read 等待數(shù)據(jù)期間, 按下ctrl + c, 觸發(fā) SIGINT 信號, 

            handler 返回后, read 被中斷, 返回 -1


            版本2, 設(shè)置 SA_RESTART 屬性:

             

            C代碼  收藏代碼
            work> gcc signal.c   
            work
            > ./a.out   
            ^Cin handler  
            handler 
            return  
            hello, world  
            read 
            13 bytes:  
            hello, world

             handler 返回后, read 系統(tǒng)調(diào)用被恢復(fù)執(zhí)行, 繼續(xù)等待數(shù)據(jù).

             

             

            六 非局部控制轉(zhuǎn)移


            int setjmp(jmp_buf env);

            int sigsetjmp(sigjmp_buf env, int savesigs);

            void longjmp(jmp_buf env, int val);

            void siglongjmp(sigjmp_buf env, int val);

            --------------------------------------------------------

            setjmp()會保存目前堆棧環(huán)境,然后將目前的地址作一個記號,

            而在程序其他地方調(diào)用 longjmp 時便會直接跳到這個記號位置,

            然后還原堆棧,繼續(xù)程序好執(zhí)行。 


            setjmp調(diào)用有點fork的味道, setjmp()return 0 if returning directly, 

            and non-zero when returning from longjmp using  the saved context.


            if (setjmp(jmpbuf)) {

               printf("return from jmp\n");

            } else {

               printf("return directly\n");

            }


            setjmp 和 sigsetjmp 的唯一區(qū)別是: setjmp 不一定會恢復(fù)信號集合,

            而sigsetjmp可以保證恢復(fù)信號集合

             

            C代碼  收藏代碼

            #include <signal.h>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <errno.h>
            #include 
            <string.h>
            #include 
            <setjmp.h>

            void sig_alrm(int signum);
            void sig_usr1(int signum);
            void print_mask(const char *str);

            static sigjmp_buf jmpbuf;
            static volatile sig_atomic_t canjmp;
            static int sigalrm_appear;

            int main(int argc, char **argv)
            {
                
            struct sigaction action, old_action;

                
            /* 設(shè)置SIGUSR1 */
                action.sa_handler 
            = sig_usr1;
                sigemptyset(
            &action.sa_mask);
                action.sa_flags 
            = 0;

                sigaction(SIGUSR1, NULL, 
            &old_action);
                
            if (old_action.sa_handler != SIG_IGN) {
                    sigaction(SIGUSR1, 
            &action, NULL);
                }

                
            /* 設(shè)置SIGALRM */
                action.sa_handler 
            = sig_alrm;
                sigemptyset(
            &action.sa_mask);
                action.sa_flags 
            = 0;

                sigaction(SIGALRM, NULL, 
            &old_action);
                
            if (old_action.sa_handler != SIG_IGN) {
                    sigaction(SIGALRM, 
            &action, NULL);
                }

                print_mask(
            "starting main:");

                
            if (sigsetjmp(jmpbuf, 1!= 0) {
                    print_mask(
            "exiting main:");
                } 
            else {
                    printf(
            "sigsetjmp return directly\n");
                    canjmp 
            = 1;
                    
            while (1) {
                        sleep(
            1);
                    }
                }

                
            return 0;
            }

            void sig_usr1(int signum)
            {
                time_t starttime;
                
            if (canjmp == 0) {
                    printf(
            "please set jmp first\n");
                    
            return;
                }

                print_mask(
            "in sig_usr1:");

                alarm(
            1);
                
            while (!sigalrm_appear);
                canjmp 
            = 0;
                siglongjmp(jmpbuf, 
            1);
            }

            void sig_alrm(int signum)
            {
                print_mask(
            "in sig_alrm:");
                sigalrm_appear 
            = 1;

                
            return;
            }

            void print_mask(const char *str) 
            {
                sigset_t sigset;
                
            int i, errno_save, flag = 0;

                errno_save 
            = errno;

                
            if (sigprocmask(0, NULL, &sigset) < 0) {
                    printf(
            "sigprocmask error\n");
                    exit(
            0);
                }

                printf(
            "%s\n", str);
                fflush(stdout);

                
            for (i = 1; i < NSIG; i++) {
                    
            if (sigismember(&sigset, i)) {
                        flag 
            = 1;
                        psignal(i, 
            "a blocked signal");
                    }
                }

                
            if (!flag) {
                    printf(
            "no blocked signal\n");
                }

                printf(
            "\n");
                errno 
            = errno_save;
            }

             

            運行結(jié)果: 

             

            C代碼  收藏代碼

            work> ./a.out &  
            [
            428483  
            starting main:  
            no blocked signal  
              
            sigsetjmp 
            return directly  
              
            kill 
            -USR1 28483  
              
            in sig_usr1:  
            a blocked signal: User defined signal 
            1  
              
            in sig_alrm:  
            a blocked signal: User defined signal 
            1  
            a blocked signal: Alarm clock  
              
            exiting main:  
            no blocked signal

             

             

            七 信號的生命周期

             

            從信號發(fā)送到信號處理函數(shù)的執(zhí)行完畢

            對于一個完整的信號生命周期(從信號發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來說,

            可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:

            信號誕生;信號在進(jìn)程中注冊完畢;信號在進(jìn)程中的注銷完畢;信號處理函數(shù)執(zhí)行完畢。


            下面闡述四個事件的實際意義:

            信號"誕生"。信號的誕生指的是觸發(fā)信號的事件發(fā)生

            (如檢測到硬件異常、定時器超時以及調(diào)用信號發(fā)送函數(shù)

            kill()或sigqueue()等)。信號在目標(biāo)進(jìn)程中"注冊";

            進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號的數(shù)據(jù)成員:

            struct sigpending pending:

            struct sigpending{

            struct sigqueue *head, **tail;

            sigset_t signal;

            };


            第三個成員是進(jìn)程中所有未決信號集,第一、第二個成員分別指向一個

            sigqueue類型的結(jié)構(gòu)鏈(稱之為"未決信號鏈表")的首尾,鏈表中

            的每個sigqueue結(jié)構(gòu)刻畫一個特定信號所攜帶的信息,并指向下一個

            sigqueue結(jié)構(gòu):

            struct sigqueue{

            struct sigqueue *next;

            siginfo_t info;

            }


            信號的注冊

            信號在進(jìn)程中注冊指的就是信號值加入到進(jìn)程的未決信號集中

            (sigpending結(jié)構(gòu)的第二個成員sigset_t signal),

            并且加入未決信號鏈表的末尾。 只要信號在進(jìn)程的未決信號集中,

            表明進(jìn)程已經(jīng)知道這些信號的存在,但還沒來得及處理,或者該信號被進(jìn)程阻塞。

            當(dāng)一個實時信號發(fā)送給一個進(jìn)程時,不管該信號是否已經(jīng)在進(jìn)程中注冊,

            都會被再注冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。

            這意味著同一個實時信號可以在同一個進(jìn)程的未決信號鏈表中添加多次. 

            當(dāng)一個非實時信號發(fā)送給一個進(jìn)程時,如果該信號已經(jīng)在進(jìn)程中注冊,

            則該信號將被丟棄,造成信號丟失。因此,非實時信號又叫做"不可靠信號"。

            這意味著同一個非實時信號在進(jìn)程的未決信號鏈表中,至多占有一個sigqueue結(jié)構(gòu).

            一個非實時信號誕生后,

            (1)、如果發(fā)現(xiàn)相同的信號已經(jīng)在目標(biāo)結(jié)構(gòu)中注冊,則不再注冊,對于進(jìn)程來說,

            相當(dāng)于不知道本次信號發(fā)生,信號丟失.

            (2)、如果進(jìn)程的未決信號中沒有相同信號,則在進(jìn)程中注冊自己。


            信號的注銷。

            在進(jìn)程執(zhí)行過程中,會檢測是否有信號等待處理

            (每次從系統(tǒng)空間返回到用戶空間時都做這樣的檢查)。如果存在未決

            信號等待處理且該信號沒有被進(jìn)程阻塞,則在運行相應(yīng)的信號處理函數(shù)前,

            進(jìn)程會把信號在未決信號鏈中占有的結(jié)構(gòu)卸掉。是否將信號從進(jìn)程未決信號集

            中刪除對于實時與非實時信號是不同的。對于非實時信號來說,由于在未決信

            號信息鏈中最多只占用一個sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信

            號在進(jìn)程未決信號集中刪除(信號注銷完畢);而對于實時信號來說,可能在

            未決信號信息鏈中占用多個sigqueue結(jié)構(gòu),因此應(yīng)該針對占用sigqueue結(jié)構(gòu)

            的數(shù)目區(qū)別對待:如果只占用一個sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號一次),

            則應(yīng)該把信號在進(jìn)程的未決信號集中刪除(信號注銷完畢)。否則,不應(yīng)該在進(jìn)程

            的未決信號集中刪除該信號(信號注銷完畢)。 

            進(jìn)程在執(zhí)行信號相應(yīng)處理函數(shù)之前,首先要把信號在進(jìn)程中注銷。


            信號生命終止。

            進(jìn)程注銷信號后,立即執(zhí)行相應(yīng)的信號處理函數(shù),執(zhí)行完畢后,

            信號的本次發(fā)送對進(jìn)程的影響徹底結(jié)束。

             

            八 關(guān)于可重入函數(shù)

             

            在信號處理函數(shù)中應(yīng)使用可重入函數(shù)。

            信號處理程序中應(yīng)當(dāng)使用可重入函數(shù)

            (注:所謂可重入函數(shù)是指一個可以被多個任務(wù)調(diào)用的過程,

            任務(wù)在調(diào)用時不必?fù)?dān)心數(shù)據(jù)是否會出錯)。因為進(jìn)程在收到信號

            后,就將跳轉(zhuǎn)到信號處理函數(shù)去接著執(zhí)行。如果信號處理函數(shù)中

            使用了不可重入函數(shù),那么信號處理函數(shù)可能會修改原來進(jìn)程中

            不應(yīng)該被修改的數(shù)據(jù),這樣進(jìn)程從信號處理函數(shù)中返回接著執(zhí)行時,

            可能會出現(xiàn)不可預(yù)料的后果。不可再入函數(shù)在信號處理函數(shù)中被視為

            不安全函數(shù)。滿足下列條件的函數(shù)多數(shù)是不可再入的:

            (1)使用靜態(tài)的數(shù)據(jù)結(jié)構(gòu),如getlogin(),gmtime(),getgrgid(),

                getgrnam(),getpwuid()以及getpwnam()等等;

            (2)函數(shù)實現(xiàn)時,調(diào)用了malloc()或者free()函數(shù);

            (3)實現(xiàn)時使用了標(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ù)時,

            首先要保存errno的值,結(jié)束時,再恢復(fù)原值。因為,信號處理過程中,

            errno值隨時可能被改變。另外,longjmp()以及siglongjmp()沒有被列為可重入函數(shù),

            因為不能保證緊接著兩個函數(shù)的其它調(diào)用是安全的。


            轉(zhuǎn)自:http://kenby.iteye.com/blog/1173862
            posted on 2013-01-13 12:31 小果子 閱讀(608) 評論(0)  編輯 收藏 引用 所屬分類: 學(xué)習(xí)筆記 、Linux
            青青青青久久精品国产h| 青青青青久久精品国产 | 久久久精品一区二区三区| 要久久爱在线免费观看| 精品人妻伦九区久久AAA片69| 精品久久久久久久| 久久精品免费观看| 99久久国产主播综合精品| 国产三级观看久久| 欧美久久综合九色综合| 久久综合色老色| 久久久噜噜噜久久中文福利| 国产V亚洲V天堂无码久久久| 亚洲国产二区三区久久| 国产伊人久久| 国产精品乱码久久久久久软件| 77777亚洲午夜久久多人| 久久精品国产亚洲AV麻豆网站 | 久久久久精品国产亚洲AV无码| 成人综合久久精品色婷婷| 亚洲午夜久久久久久久久电影网| 无码超乳爆乳中文字幕久久| 国产精品一久久香蕉产线看| 精品久久人人爽天天玩人人妻| 日本久久久久久久久久| 色欲综合久久中文字幕网| 久久精品国产久精国产思思| 亚洲综合久久综合激情久久| 久久夜色精品国产亚洲| 国产成人精品久久二区二区| 久久久人妻精品无码一区| 久久无码人妻一区二区三区| 国内精品久久久久久久coent| 精品多毛少妇人妻AV免费久久| 国产精品久久久久jk制服| 伊人色综合九久久天天蜜桃 | 久久人爽人人爽人人片AV| 国产精品gz久久久| 久久久国产乱子伦精品作者 | 亚洲色欲久久久久综合网| 99久久综合狠狠综合久久止|