天天看點

《unix進階環境程式設計》信号——信号集

信号集

          信号集是表示多個信号的資料類型,這裡的信号集資料類型是 sigset_t,包含五個處理信号集的函數:

/* 信号集 */

#include <signal.h>

int sigemptyset(sigset_t *set);//初始化由set所指向的信号集,清空信号集;
int sigfillset(sigset_t *set);//初始化由set所指向的信号集,使其包括所有信号;
int sigaddset(sigset_t *set, int signo);//把指定的信号signo添加到由set所指的信号集中;
int sigdelset(sigset_t *set, int signo);//把指定的信号signo從由set所指定的信号集中删除;
//前面四個函數傳回值:若成功則傳回0,若出錯則傳回-1;

int sigismember(const sigset_t *set, int signo);//判斷指定的信号signo是否在由set所指的信号集中;
//傳回值:若為真則傳回1,若為假則傳回0,若出錯則傳回-1;
/*
 * 說明:
 * 所有應用程式使用信号集之前,要對該信号集調用sigemptyset或sigfillset一次;
 */
           

sigprocmask 函數

        在前面我們提到,task_struct  結構有一個blocked 成員(我們稱之為“信号屏蔽字”),它指定了程序阻塞的信号,被阻塞的信号将不能被遞送給程序,直到程序解除阻塞。在信号被阻塞時,核心将其放置到待決清單上。如果同一個信号在阻塞期間被發送了多次,則在待決清單中隻放置一次。也就是說,不管發送了多少相同的信号,在程序删除阻塞後,都隻會接收到一個信号。調用函數sigprocmask 可以檢測或更改其信号屏蔽字。在調用 sigprocmask 後如果有任何未決的、不再阻塞的信号,則在 sigprocmask 傳回前,至少會将其中一個信号遞送給該程序。

/* sigprocmask 函數 */
/*
 * 函數功能:檢查或更改信号屏蔽字,也可同時執行這兩個操作;
 * 傳回值:若成功則傳回0,若出錯則傳回-1;
 * 函數原型:
 */
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
/*
 * 說明:
 * 若oset是非空指針,那麼程序的目前信号屏蔽字通過oset傳回;
 * 若set是非空指針,則參數how訓示如何修改目前信号屏蔽字;
 * 若set是空指針,則不改變該程序的信号屏蔽字,how的值就沒有意義;
 * 參數how可選以下值:
 * (1)SIG_BLOCK   該程序新的信号屏蔽字是其目前信号屏蔽字和set指向信号集的并集。set包含了我們希望阻塞的附加信号;
 * (2)SIG_UNBLOCK 該程序新的信号屏蔽字是其目前信号屏蔽字和set指向信号集補集的交集;set包含我們希望解除阻塞的附加信号;
 * (3)SIG_SETMASK 該程序新的信号屏蔽字将被set指向的信号集的值所代替;
 *  SIG_BLOCK是"或"操作,而SIG_SETMASK則是指派操作;
 *  注意:SIGKILL 和 SIGSTOP 信号是不能阻塞的;
 */
           

sigpending 函數

          sigpending 函數傳回信号集,其中的各個信号對于調用程序是阻塞的而不能傳遞,該信号集通過set參數傳回。

/* sigpending函數 */
/*
 * 函數功能:傳回信号集;
 * 傳回值:若成功則傳回0,若出錯則傳回-1;
 * 函數原型:
 */
#include <signal.h>
int sigpending(sigset_t *set);
           

測試程式:

#include "apue.h"
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>

static void sig_quit(int signo);

int main()
{
    sigset_t    newmask,oldmask,pendmask;
    if(signal(SIGQUIT,sig_quit) == SIG_ERR)
    {
        err_sys("signal() error");
        exit(-1);
    }
    //初始化信号集
    sigemptyset(&newmask);
    //添加一個SIGQUIT信号
    sigaddset(&newmask,SIGQUIT);
    //将newmask信号集設定為阻塞,原信号集儲存在oldmask中
    if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) == -1)
    {
        err_sys("SIG_BLOCK error");
        exit(-1);
    }
    sleep(5);
    //擷取阻塞的信号集
    if(sigpending(&pendmask) == -1)
    {
        err_sys("sigpending() error");
        exit(-1);
    }
    //判斷SIGQUIT是否是阻塞的
    if(sigismember(&pendmask,SIGQUIT))
        printf("\nSIGQUIT is pending.\n");
    //恢複原始的信号集
    if(sigprocmask(SIG_SETMASK,&oldmask,NULL) == -1)
    {
        err_sys("SIG_SETMASK error");
        exit(-1);
    }
    printf("SITQUIT unblocked\n");
    sleep(5);
    exit(0);
}

static void sig_quit(int signo)
{
    printf("caught SIGQUIT.\n");
    if(signal(SIGQUIT,SIG_DFL) == SIG_ERR)
    {
        err_sys("can't reset SIGQUIT");
        exit(-1);
    }
}
           

輸出結果:

$ ./sigset 
^\

SIGQUIT is pending.
caught SIGQUIT.
SITQUIT unblocked

$ ./sigset 
^\^\^\^\^\^\

SIGQUIT is pending.
caught SIGQUIT.
SITQUIT unblocked
           

       在程式第二次  sleep 時,産生了多個 SIGQUIT 信号,此時被 pending,解除了  mask 後,隻産生了一次 action,也說明了在同一時刻産生多次同一種信号,不會對信号排隊。

參考資料:

《UNIX進階環境程式設計》

繼續閱讀