• <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>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            轉(zhuǎn)自http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html,作者:鄭彥興

            一、信號生命周期

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

            對于一個完整的信號生命周期(從信號發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:信號誕生;信號在進(jìn)程中注冊完畢;信號在進(jìn)程中的注銷完畢;信號處理函數(shù)執(zhí)行完畢。相鄰兩個事件的時間間隔構(gòu)成信號生命周期的一個階段。


             

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

            1. 信號"誕生"。信號的誕生指的是觸發(fā)信號的事件發(fā)生(如檢測到硬件異常、定時器超時以及調(diào)用信號發(fā)送函數(shù)kill()或sigqueue()等)。
            2. 信號在目標(biāo)進(jìn)程中"注冊";進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號的數(shù)據(jù)成員:
              1. 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):
              1. struct sigqueue{
              2.     struct sigqueue *next;
              3.     siginfo_t info;
              4. }
              信號在進(jìn)程中注冊指的就是信號值加入到進(jìn)程的未決信號集中(sigpending結(jié)構(gòu)的第二個成員sigset_t signal),并且信號所攜帶的信息被保留到未決信號信息鏈的某個sigqueue結(jié)構(gòu)中。 只要信號在進(jìn)程的未決信號集中,表明進(jìn)程已經(jīng)知道這些信號的存在,但還沒來得及處理,或者該信號被進(jìn)程阻塞。

              注: 
              當(dāng)一個實時信號發(fā)送給一個進(jìn)程時,不管該信號是否已經(jīng)在進(jìn)程中注冊,都會被再注冊一次,因此,信號不會丟失,因此,實時信號又叫做"可靠信號"。這意味著 同一個實時信號可以在同一個進(jìn)程的未決信號信息鏈中占有多個sigqueue結(jié)構(gòu)(進(jìn)程每收到一個實時信號,都會為它分配一個結(jié)構(gòu)來登記該信號信息,并把 該結(jié)構(gòu)添加在未決信號鏈尾,即所有誕生的實時信號都會在目標(biāo)進(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)程中注冊自己)。

            3. 信號在進(jìn)程中的注銷。在目標(biāo)進(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)程中注銷。
            4. 信號生命終止。進(jìn)程注銷信號后,立即執(zhí)行相應(yīng)的信號處理函數(shù),執(zhí)行完畢后,信號的本次發(fā)送對進(jìn)程的影響徹底結(jié)束。

              注: 
              1)信號注冊與否,與發(fā)送信號的函數(shù)(如kill()或sigqueue()等)以及信號安裝函數(shù)(signal()及 sigaction())無關(guān),只與信號值有關(guān)(信號值小于SIGRTMIN的信號最多只注冊一次,信號值在SIGRTMIN及SIGRTMAX之間的信 號,只要被進(jìn)程接收到就被注冊)。 
              2)在信號被注銷到相應(yīng)的信號處理函數(shù)執(zhí)行完畢這段時間內(nèi),如果進(jìn)程又收到同一信號多次,則對實時信號來說,每一次都會在進(jìn)程中注冊;而對于非實時信號來說,無論收到多少次信號,都會視為只收到一個信號,只在進(jìn)程中注冊一次。


            二、信號編程注意事項

            1. 防止不該丟失的信號丟失。如果對八中所提到的信號生命周期理解深刻的話,很容易知道信號會不會丟失,以及在哪里丟失。
            2. 程序的可移植性 
              考慮到程序的可移植性,應(yīng)該盡量采用POSIX信號函數(shù),POSIX信號函數(shù)主要分為兩類:
              • POSIX 1003.1信號函數(shù): Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。
              • POSIX 1003.1b信號函數(shù)。POSIX 1003.1b在信號的實時性方面對POSIX 1003.1做了擴(kuò)展,包括以下三個函數(shù): sigqueue()、sigtimedwait()、sigwaitinfo()。 其中,sigqueue主要針對信號發(fā)送,而sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函數(shù),后面有相應(yīng)實例。
                #include <signal.h> int sigwaitinfo(sigset_t *set, siginfo_t *info).

                該函數(shù)與sigsuspend()類似,阻塞一個進(jìn)程直到特定信號發(fā)生,但信號到來時不執(zhí)行信號處理函數(shù),而是返回信號值。因此為了避免執(zhí)行相應(yīng)的信號處理函數(shù),必須在調(diào)用該函數(shù)前,使進(jìn)程屏蔽掉set指向的信號,因此調(diào)用該函數(shù)的典型代碼是:
                sigset_t newmask; int rcvd_sig; siginfo_t info; sigemptyset(&newmask); sigaddset(&newmask, SIGRTMIN); sigprocmask(SIG_BLOCK, &newmask, NULL); rcvd_sig = sigwaitinfo(&newmask, &info) if (rcvd_sig == -1) { .. }

                調(diào)用成功返回信號值,否則返回-1。sigtimedwait()功能相似,只不過增加了一個進(jìn)程等待的時間。
            3. 程序的穩(wěn)定性。 
              為了增強程序的穩(wěn)定性,在信號處理函數(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)用是安全的。


            三、深入淺出:信號應(yīng)用實例

            linux下的信號應(yīng)用并沒有想象的那么恐怖,程序員所要做的最多只有三件事情:

            1. 安裝信號(推薦使用sigaction());
            2. 實現(xiàn)三參數(shù)信號處理函數(shù),handler(int signal,struct siginfo *info, void *);
            3. 發(fā)送信號,推薦使用sigqueue()。

            實際上,對有些信號來說,只要安裝信號就足夠了(信號處理方式采用缺省或忽略)。其他可能要做的無非是與信號集相關(guān)的幾種操作。

            實例一:信號發(fā)送及處理 
            實現(xiàn)一個信號接收程序sigreceive(其中信號安裝由sigaction())。

            1. #include <signal.h>
            2. #include <sys/types.h>
            3. #include <unistd.h>
            4. void new_op(int,siginfo_t*,void*);
            5. int main(int argc,char**argv)
            6. {
            7.     struct sigaction act;    
            8.     int sig;
            9.     sig=atoi(argv[1]);
            10.     
            11.     sigemptyset(&act.sa_mask);
            12.     act.sa_flags=SA_SIGINFO;
            13.     act.sa_sigaction=new_op;
            14.     
            15.     if(sigaction(sig,&act,NULL) < 0)
            16.     {
            17.         printf("install sigal error\n");
            18.     }
            19.     
            20.     while(1)
            21.     {
            22.         sleep(2);
            23.         printf("wait for the signal\n");
            24.     }
            25. }
            26. void new_op(int signum,siginfo_t *info,void *myact)
            27. {
            28.     printf("receive signal %d", signum);
            29.     sleep(5);
            30. }

            說明,命令行參數(shù)為信號值,后臺運行sigreceive signo &,可獲得該進(jìn)程的ID,假設(shè)為pid,然后再另一終端上運行kill -s signo pid驗證信號的發(fā)送接收及處理。同時,可驗證信號的排隊問題。 
            注:可以用sigqueue實現(xiàn)一個命令行信號發(fā)送程序sigqueuesend,見 附錄1

            實例二:信號傳遞附加信息 
            主要包括兩個實例:

            1. 向進(jìn)程本身發(fā)送信號,并傳遞指針參數(shù);
              1. #include <signal.h>
              2. #include <sys/types.h>
              3. #include <unistd.h>
              4. void new_op(int,siginfo_t*,void*);
              5. int main(int argc,char**argv)
              6. {
              7.     struct sigaction act;    
              8.     union sigval mysigval;
              9.     int i;
              10.     int sig;
              11.     pid_t pid;        
              12.     char data[10];
              13.     memset(data,0,sizeof(data));
              14.     for(i=0;< 5;i++)
              15.         data[i]='2';
              16.     mysigval.sival_ptr=data;
              17.     
              18.     sig=atoi(argv[1]);
              19.     pid=getpid();
              20.     
              21.     sigemptyset(&act.sa_mask);
              22.     act.sa_sigaction=new_op;//三參數(shù)信號處理函數(shù)
              23.     act.sa_flags=SA_SIGINFO;//信息傳遞開關(guān)
              24.     if(sigaction(sig,&act,NULL) < 0)
              25.     {
              26.         printf("install sigal error\n");
              27.     }
              28.     while(1)
              29.     {
              30.         sleep(2);
              31.         printf("wait for the signal\n");
              32.         sigqueue(pid,sig,mysigval);//向本進(jìn)程發(fā)送信號,并傳遞附加信息
              33.     }
              34. }
              35. void new_op(int signum,siginfo_t *info,void *myact)//三參數(shù)信號處理函數(shù)的實現(xiàn)
              36. {
              37.     int i;
              38.     for(i=0;i<10;i++)
              39.     {
              40.         printf("%c\n ",(*( (char*)((*info).si_ptr)+i)));
              41.     }
              42.     printf("handle signal %d over;",signum);
              43. }

              這個例子中,信號實現(xiàn)了附加信息的傳遞,信號究竟如何對這些信息進(jìn)行處理則取決于具體的應(yīng)用。

            2. 2、 不同進(jìn)程間傳遞整型參數(shù):把1中的信號發(fā)送和接收放在兩個程序中,并且在發(fā)送過程中傳遞整型參數(shù)。 
              信號接收程序:
              1. #include <signal.h>
              2. #include <sys/types.h>
              3. #include <unistd.h>
              4. void new_op(int,siginfo_t*,void*);
              5. int main(int argc,char**argv)
              6. {
              7.     struct sigaction act;
              8.     int sig;
              9.     pid_t pid;        
              10.     
              11.     pid=getpid();
              12.     sig=atoi(argv[1]);    
              13.     
              14.     sigemptyset(&act.sa_mask);
              15.     act.sa_sigaction=new_op;
              16.     act.sa_flags=SA_SIGINFO;
              17.     if(sigaction(sig,&act,NULL)<0)
              18.     {
              19.         printf("install sigal error\n");
              20.     }
              21.     while(1)
              22.     {
              23.         sleep(2);
              24.         printf("wait for the signal\n");
              25.     }
              26. }
              27. void new_op(int signum,siginfo_t *info,void *myact)
              28. {
              29.     printf("the int value is %d \n",info->si_int);
              30. }

              信號發(fā)送程序:命令行第二個參數(shù)為信號值,第三個參數(shù)為接收進(jìn)程ID。

              1. #include <signal.h>
              2. #include <sys/time.h>
              3. #include <unistd.h>
              4. #include <sys/types.h>
              5. main(int argc,char**argv)
              6. {
              7.     pid_t pid;
              8.     int signum;
              9.     union sigval mysigval;
              10.     signum=atoi(argv[1]);
              11.     pid=(pid_t)atoi(argv[2]);
              12.     mysigval.sival_int=8;//不代表具體含義,只用于說明問題
              13.     if(sigqueue(pid,signum,mysigval)==-1)
              14.         printf("send error\n");
              15.     sleep(2);
              16. }

              注:實例2的兩個例子側(cè)重點在于用信號來傳遞信息,目前關(guān)于在linux下通過信號傳遞信息 的實例非常少,倒是Unix下有一些,但傳遞的基本上都是關(guān)于傳遞一個整數(shù),傳遞指針的我還沒看到。我一直沒有實現(xiàn)不同進(jìn)程間的指針傳遞(實際上更有意 義),也許在實現(xiàn)方法上存在問題吧,請實現(xiàn)者email我。

            實例三:信號阻塞及信號集操作

            1. #include "signal.h"
            2. #include "unistd.h"
            3. static void my_op(int);
            4. main()
            5. {
            6.     sigset_t new_mask,old_mask,pending_mask;
            7.     struct sigaction act;
            8.     sigemptyset(&act.sa_mask);
            9.     act.sa_flags=SA_SIGINFO;
            10.     act.sa_sigaction=(void*)my_op;
            11.     if(sigaction(SIGRTMIN+10,&act,NULL))
            12.         printf("install signal SIGRTMIN+10 error\n");
            13.     sigemptyset(&new_mask);
            14.     sigaddset(&new_mask,SIGRTMIN+10);
            15.     if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask))
            16.         printf("block signal SIGRTMIN+10 error\n");
            17.     sleep(10);    
            18.     printf("now begin to get pending mask and unblock SIGRTMIN+10\n");
            19.     if(sigpending(&pending_mask)<0)
            20.         printf("get pending mask error\n");
            21.     if(sigismember(&pending_mask,SIGRTMIN+10))
            22.         printf("signal SIGRTMIN+10 is pending\n");
            23.     if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0)
            24.         printf("unblock signal error\n");
            25.     printf("signal unblocked\n");
            26.     sleep(10);
            27. }
            28. static void my_op(int signum)
            29. {
            30.     printf("receive signal %d \n",signum);
            31. }

            編譯該程序,并以后臺方式運行。在另一終端向該進(jìn)程發(fā)送信號(運行kill -s 42 pid,SIGRTMIN+10為42),查看結(jié)果可以看出幾個關(guān)鍵函數(shù)的運行機制,信號集相關(guān)操作比較簡單。

            注:在上面幾個實例中,使用了printf()函數(shù),只是作為診斷工具,pringf()函數(shù)是不可重入的,不應(yīng)在信號處理函數(shù)中使用。


            結(jié)束語:

            系統(tǒng)地對linux信號機制進(jìn)行分析、總結(jié)使我受益匪淺!感謝王小樂等網(wǎng)友的支持! 
            Comments and suggestions are greatly welcome!


            附錄1:

            用sigqueue實現(xiàn)的命令行信號發(fā)送程序sigqueuesend,命令行第二個參數(shù)是發(fā)送的信號值,第三個參數(shù)是接收該信號的進(jìn)程ID,可以配合實例一使用:

            1. #include <signal.h>
            2. #include <sys/types.h>
            3. #include <unistd.h>
            4. int main(int argc,char**argv)
            5. {
            6.     pid_t pid;
            7.     int sig;
            8.     sig=atoi(argv[1]);
            9.     pid=atoi(argv[2]);
            10.     sigqueue(pid,sig,NULL);
            11.     sleep(2);
            12. }


            參考資料

            91精品国产高清久久久久久91| 久久亚洲国产精品成人AV秋霞| 精品久久久久久亚洲精品| 久久99国产精一区二区三区 | 久久精品无码一区二区日韩AV| 国产午夜精品理论片久久| 久久人与动人物a级毛片| 狠色狠色狠狠色综合久久| 久久99精品国产99久久6| 18岁日韩内射颜射午夜久久成人| 国内精品久久久久久久97牛牛 | 99久久免费国产精品特黄| 久久99国内精品自在现线| 亚洲人AV永久一区二区三区久久 | 国内精品久久久久| 中文精品久久久久人妻| 久久精品国产91久久综合麻豆自制 | 欧美日韩精品久久久久| 97久久精品国产精品青草| 中文字幕热久久久久久久| 久久久久久无码国产精品中文字幕 | 欧美一区二区精品久久| 久久久久国产精品嫩草影院| 久久九九免费高清视频| 国产韩国精品一区二区三区久久| 久久精品国产亚洲AV香蕉| 久久久久国产视频电影| 国产激情久久久久影院小草| 国内精品久久久久| 91精品国产综合久久婷婷| 日韩人妻无码精品久久免费一| 久久久久久久91精品免费观看| 久久久久亚洲精品男人的天堂| 国产精品伦理久久久久久| 久久中文娱乐网| 国产成人香蕉久久久久| 9191精品国产免费久久| 国产精品va久久久久久久| 久久综合欧美成人| 一级做a爰片久久毛片人呢| 91久久精品91久久性色|