歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> sigsuspend()函數 解釋

sigsuspend()函數 解釋

日期:2017/3/3 13:03:07   编辑:Linux技術

sigsuspend函數作用 :如果在等待信號發生時希望去休眠,則使用sigsuspend函數是非常合適的

頭文件:#include <signal.h>

一個保護臨界區代碼的錯誤實例:(sigprocmask()和pause()實現)

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
 
void handler(int sig)    //信號處理函數的實現
{
   printf("SIGINT sig");
}
int main()
{
    sigset_t new,old;
    struct sigaction act;
    act.sa_handler = handler;  //信號處理函數handler
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT, &act, 0);  //准備捕捉SIGINT信號
    sigemptyset(&new);
    sigaddset(&new, SIGINT);
    sigprocmask(SIG_BLOCK, &new, &old);  //將SIGINT信號阻塞,同時保存當前信號集
    printf("Blocked");
    sigprocmask(SIG_SETMASK, &old, NULL);  //取消阻塞
    pause();
    return 0;
}
上面實例的問題是:本來期望pause()之後,來SIGINT信號,可以結束程序;可是,如果當“取消阻塞”和“pause”之間,正好來了SIGINT信號,結果程序因為pause的原因會一直掛起。。。

如果在信號阻塞時將其發送給進程,那麼該信號的傳遞就被推遲直到對它解除了阻塞。對應用程序而言,該信號好像發生在解除對SIGINT的阻塞和pause之間。如果發生了這種情況,或者如果在解除阻塞時刻和pause之間確實發生了信號,那麼就產生了問題。因為我們可能不會再見到該信號,所以從這種意義上而言,在此時間窗口(解除阻塞和pause之間)中發生的信號丟失了,這樣就使pause永遠阻塞。

為了糾正此問題,需要在一個原子操作中先恢復信號屏蔽字,然後使進程休眠。這種功能是由sigsuspend函數提供的。

#include <signal.h>

int sigsuspend( const sigset_t *sigmask );
返回值:-1,並將errno設置為EINTR
將進程的信號屏蔽字設置為由sigmask指向的值。在捕捉到一個信號或發生了一個會終止該進程的信號之前,該進程被掛起。如果捕捉到一個信號而且從該信號處理程序返回,則sigsuspend返回,並且將該進程的信號屏蔽字設置為調用sigsuspend之前的值。

注意,此函數沒有成功返回值。如果它返回到調用者,則總是返回-1,並將errno設置為EINTR(表示一個被中斷的系統調用)。

3)使用sigsuspend()的程序

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig)   //信號處理程序
{
   if(sig == SIGINT)
      printf("SIGINT sig");
   else if(sig == SIGQUIT)
      printf("SIGQUIT sig");
   else
      printf("SIGUSR1 sig");
}
 
int main()
{
    sigset_t new,old,wait;   //三個信號集
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT, &act, 0);    //可以捕捉以下三個信號:SIGINT/SIGQUIT/SIGUSR1
    sigaction(SIGQUIT, &act, 0);
    sigaction(SIGUSR1, &act, 0);
   
    sigemptyset(&new);
    sigaddset(&new, SIGINT);  //SIGINT信號加入到new信號集中
    sigemptyset(&wait);
    sigaddset(&wait, SIGUSR1);  //SIGUSR1信號加入wait
    sigprocmask(SIG_BLOCK, &new, &old);       //將SIGINT阻塞,保存當前信號集到old中
   
    //臨界區代碼執行    
  
    if(sigsuspend(&wait) != -1)  //程序在此處掛起;用wait信號集替換new信號集。即:過來SIGUSR1信  號,阻塞掉,程序繼續掛起;過來其他信號,例如SIGINT,則會喚醒程序。執行sigsuspend的原子操作。注意:如果“sigaddset(&wait, SIGUSR1);”這句沒有,則此處不會阻塞任何信號,即過來任何信號均會喚醒程序。
        printf("sigsuspend error");
    printf("After sigsuspend");
    sigprocmask(SIG_SETMASK, &old, NULL);
    return 0;
}
sigsuspend的原子操作是:

(1)設置新的mask阻塞當前進程(上面是用wait替換new,即阻塞SIGUSR1信號)

(2)收到SIGUSR1信號,阻塞,程序繼續掛起;收到其他信號,恢復原先的mask(即包含SIGINT信號的)。

(3)調用該進程設置的信號處理函數(程序中如果先來SIGUSR1信號,然後過來SIGINT信號,則信號處理函數會調用兩次,打印不同的內容。第一次打印SIGINT,第二次打印SIGUSR1,因為SIGUSR1是前面阻塞的)

(4)待信號處理函數返回,sigsuspend返回了。(sigsuspend將捕捉信號和信號處理函數集成到一起了)

注:sigsuspend實際是將sigprocmask和pause結合起來原子操作。

轉自:http://blog.sina.com.cn/s/blog_6af9566301013xp4.html 和http://www.cnblogs.com/nufangrensheng/category/543313.html

Copyright © Linux教程網 All Rights Reserved