AIX信號初探

AIX 7.1,xl C/C++ 13.1.0

UNIX/LINUX信號方面涉及若干函數,如signal、sigcation、setjmp、longjmp函數,還有kill、pause、wait等函數。有關這些函數的介紹網上已經很多了,不再贅述。

下面我們先來看看signal和setjmp、longjmp的使用案例。考慮如下代碼:

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>

jmp_buf g_sjbuf;

void onintr(int s);

int main(int argc, char * argv[])
{
    if(SIG_IGN != signal(SIGINT, SIG_IGN))
    {
        printf("!= SIG_IGN\n");    <span style="color:#3333ff;">// <<<<<<<<<< 會不會打印出來?</span>
        signal(SIGINT, onintr);
    }
    
    //setjmp(g_sjbuf);             <span style="color:#3333ff;">// <<<<<<<<<< 甲處</span>
    for(int i = 0; ; i++)
    {
        //setjmp(g_sjbuf);      <span style="background-color: rgb(204, 204, 204);">   <span style="color:#3333ff;">// <<<<<<<<<< 乙處</span>   </span>
        printf("for = %d\n", i);
        sleep(2);
        //setjmp(g_sjbuf);         <span style="color:#3333ff;">// <<<<<<<<<< 丙處</span>
    }
    return 0; 
}

void onintr(int s)
{
    signal(SIGINT, onintr);
    printf("\nInterrupt s = %d\n", s);
    longjmp(g_sjbuf, 0);
    return;
}

編譯,運行。

首先,!= SIG_IGN會不會被打印出來?

結果:會。

程序一開始運行時,調用signal返回的是上一次處理該信號的回調函數(返回的是一個函數指針,這種類型:void(*)(int)),由於這是程序進來第一次調用signal,所以返回的應該是系統處理該信號的“方法”,就是默認的處理方式,當然不是SIG_IGN(忽略),所以printf就被執行了。那這返回值是個啥玩意兒呢?把相關代碼改成這樣(順便看看SIG_IGN是個什麼鬼):

    void (* pVal)(int) = signal(SIGINT, SIG_IGN);
    printf("pVal = 0x%X, SIG_IGN = 0x%X\n", (int)pVal, (int)SIG_IGN);
    if(pVal != SIG_IGN)

瞅瞅。

結果:

# ./a.out                                  
pVal = 0x0, SIG_IGN = 0x1
!= SIG_IGN                            
for = 0                                     
...                                              

那麼就很清楚了,SIG_IGN這個函數指針的值是1,系統默認處理SIGINT的函數指針是0(居然是0)。


第一個問題搞清楚了,我們再來研究中斷返回的問題。

調用signal把SIGINT信號的服務程序註冊為onintr函數,當該信號到來時,就會跳到onintr函數執行(要不要看看彙編代碼?嗷,算了吧),然後返回,返回點是onintr函數尾部的這麼一句:

longjmp(g_sjbuf, 0);

簡單來說,longjmp與setjmp配對使用,後者保存中斷前的狀態(就算是上下文吧),由longjmp跳轉回去。這裡畢竟跟操作系統的任務切換還是不同的,操作系統通過處理器的如壓棧、關中斷等等相關指令保存上下文、切換任務,都不需要程序關心,操作系統幫你做了,而在這裡的信號處理需要程序員手動指定在哪而保存,在哪返回。

假設上述代碼中的for循環是程序主要要做的事,按照常規思維通過signal函數設置好信號服務程序(這裡套用中斷服務程序的概念)後,就可以“保存現場”以供返回了,也就是代碼的甲處,實際上,書上也是這麼寫的。我們來看看這樣寫的運行結果:

# ./a.out                                               
pVal = 0x0, SIG_IGN = 0x1             
!= SIG_IGN                                         
for = 0                                                  
for = 1                                                  
for = 2                                                  
for = 3                                                                                     
                                                                                                
                                                                                                
Interrupt s = 2            
<<<<按下了CRTL + C鍵               
for = 0                          <<<<for從頭開始了                         
for = 1                                                                                     
for = 2                       
for = 3                       
for = 4                       
for = 5                       
                                   
                                   
Interrupt s = 2                                                                       
for = 0                          <<<<再次從頭開始                         
for = 1                                                                                    
                                   
                                   
Interrupt s = 2          
for = 0                        
for = 1                        
for = 2                        
for = 3                                                                                   
Quit(coredump)       <<<<按下CRTL + /鍵,結束程序  
#                                                                                            

可見,每當按下CTRL + C鍵觸發SIGINT中斷,進入onintr函數打印Interrupts = 2語句,然後返回,返回到setjmp的位置,也就是甲處,所以for又得重頭來。剛才說過,for裡面是程序的主要任務,比如複雜的科學計算、氣象預報、飛彈執導、反擊外星人什麼的,這樣一搞就重來了,外星人都要被揍一把,顯然不合理啊。所以我們把setjmp移到乙處。

# ./a.out                                           
pVal = 0x0, SIG_IGN = 0x1         
!= SIG_IGN                                     
for = 0                                              
for = 1                                              
for = 2                                              
for = 3                                              
                                                         
                                                                                                          
Interrupt s = 2              <<<<按下了CRTL + C鍵                        
for = 3                           <<<<什麼?這不是上次的結果嗎?      
for = 4                                                                                               
                                                                                                          
                                                                                                          
Interrupt s = 2                                                                                 
for = 4                           <<<<再次證明                                          
for = 5                                                                                              
for = 6                                                                                              
Quit(coredump)                                                                             
#                                                                                                       


結果表明for在onintr返回後重複循環了一次,這也不太好。仔細研究發現這是由於乙處在循環的開頭,所以onintr執行完成後就有返回到當次循環的開頭了,所以重複執行那次循環。既然這樣,就把setjmp移到循環的尾部去,就像丙處那樣!

# ./a.out                                               
pVal = 0x0, SIG_IGN = 0x1             
!= SIG_IGN                                         
for = 0                                                  
for = 1                                                  
for = 2                                                                                     
for = 3                                <<<<注意,這裡是3                  
                                                                                                 
                                                                                                 
Interrupt s = 2                  <<<<按下了CRTL + C鍵           
for = 4                               <<<<哈哈,接上了                     
for = 5                                                                                     
for = 6                                                  
                                                              
                                                                                                 
Interrupt s = 2                                                                        
for = 7                                  <<<<再試一把,也是正確的  
for = 8                                                                                      
for = 9                                                                                      
for = 10                                                 
for = 11                                                 
                                                              
                                                              
Interrupt s = 2                                      
for = 12                                                 
Quit(coredump)                                  
#                                                            


很爽,結果對了!可想想看書上為啥寫在甲處呢?丙處會不會太晚了呢?循環的尾部,也就是說在打了一遍外星人,再保存現場,那得耽誤多少時間。在這之前如果來了SIGINT信號->onintr函數->返回setjmp處,可還沒setjmp,這會出現啥情況呢?試試(不用重新編譯了,在setjmp之前按下CTRL + C就行)。。

# ./a.out                                                                                                              
pVal = 0x0, SIG_IGN = 0x1                                                                             
!= SIG_IGN                                                                                                         
for = 0                            <<<<剛一printf就按CTRL+ C                                   
                                                                                                                             
                                                                                                                             
Interrupt s = 2            <<<<觸發了SIGINT信號                                               
Segmentation fault(coredump)        <<<<該返回了,出錯!                     
#                                                                                                                           

程序出錯了,還沒setjmp就longjmp,引發了個分段錯誤,屬於內存錯誤的一種,致命錯誤,cpu不知道該從哪執行了,所以進程被終止了。可以進一步研究,其實不是cpu不知道該從哪執行,g_sjbuf沒被初始化,裡面的值是未知的(不知道是不是像windows那樣的“燙燙燙”哈),cpu就按那裡面的內容解讀,跑到一個啥地方去執行了,這大概率是要出錯的(不是絕對哈,概率我沒算過。。),結果果真錯了,程序終止了。

這該如何是好?!甲乙丙三處setjmp都不好!

剛才我改代碼,無意少加了一處注釋,導致甲和丙的同時存在,結果。。。挺好使!

我們先看看代碼,一上來設置完“信號服務程序”就保存一把現場,這是能做到的最早的setjmp的地方了,有信號過來,就不怕了(有地兒返回),而且還沒進入循環,返回的地點也是正確的。然後循環開始做事,不能保存,一保存會造成重複循環;循環到尾部,保存一把,即丙處,這樣就算信號來了,也不會重複循環,因為本輪循環已經要結束了;下一輪循環也是一樣,不會有問題。測一把:

# ./a.out                                         
pVal = 0x0, SIG_IGN = 0x1       
!= SIG_IGN                                   
for = 0                            <<<<剛一printf就按CTRL+ C                    
                                                       
                                                                    
Interrupt s = 2               <<<<沒完蛋                                                      
for = 0                       <<<<出現了一次重複                      
for = 1                                                                                  
for = 2                                                              
for = 3                                        
for = 4                                   
                                            
                                                
Interrupt s = 2                      
for = 5                               <<<<以後就都正常了                    
for = 6                                                    
                                               
                                                    
Interrupt s = 2         
for = 7                          
Quit(coredump)      
#                               


沒出現段錯誤,但有一次重複,就是第一次,分析代碼發現第一次重複不可避免,後面的不會了,測試結果也表明如此。還有個問題,在某次循環當中發生SIGINT信號,會回到上次循環末執行,也就是中間可能有一部分代碼還是會被重複執行。能否避免呢?——除非你運行一行代碼就setjmp一把,步步保存,這肯定不可行。還需探索更好的方式。


http://blog.csdn.net/ncepubdtb/article/details/42458087

可以一讀。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值