UNIX環境編程學習筆記(24)——信號處理進階學習之信號集和進程信號屏蔽字


lienhua34
2014-11-03

1 信號傳遞過程

信號源為目標進程產生了一個信號,然后由內核來決定是否要將該信號傳遞給目標進程。從信號產生到傳遞給目標進程的流程圖如圖 1 所示,

圖 1: 信號產生、傳遞到處理的流程圖

進程可以阻塞信號的傳遞。當信號源為目標進程產生了一個信號之后,內核會執行依次執行下面操作,

1. 如果目標進程設置了忽略該信號,則內核直接將該信號丟棄。

2. 如果目標進程沒有阻塞該信號,則內核將該信號傳遞給目標進程,由目標進程執行相對應操作。

3. 如果目標進程設置阻塞該信號,則內核將該信號放到目標進程的阻塞信號列表中,等待目標進程對該類型信號的下一步設置。若目標進程后續設置忽略該信號,則內核將該信號從目標進程的阻塞信號列表中移除並丟棄。若目標進程對該信號解除了阻塞,內核將該信號傳遞給目標進程進行相對應的操作。

在信號產生到信號傳遞給目標進程之間的時間間隔內,我們稱該信號為未決的(pending)。

每個進程都有一個信號屏蔽字(signal mask),它規定了當前要阻塞傳遞給該進程的信號集。對於每種可能的信號,信號屏蔽字中都有一位與之對應。

2 信號集及其操作

POSIX.1 定義了一個數據類型sigset_t,用於表示信號集。另外,頭文件 signal.h 提供了下列五個處理信號集的函數。

函數 sigemptyset 初始化由 set 指向的信號集,清除其中所有信號。

int sigemptyset(sigset_t *set);

返回值:若成功則返回0,若出錯則返回-1

函數 sigfillset 初始化由 set 指向的信號集,使其包含所有信號。

int sigfillset(sigset_t *set);
返回值:若成功則返回0,若出錯則返回-1

函數 sigaddset 將一個信號 signo 添加到現有信號集 set 中。

int sigaddset(sigset_t *set, int signo);
返回值:若成功則返回0,若出錯則返回-1

函數 sigdelset 將一個信號 signo 從信號集 set 中刪除。

int sigdelset(sigset_t *set, int signo);
返回值:若成功則返回0,若出錯則返回-1

函數 sigismember 判斷指定信號 signo 是否在信號集 set 中。

int sigismember(const sigset_t *set, int signo);
返回值:若真則返回1,若假則返回0,若出錯則返回-1

3. sigprocmask 檢 或設置進程的信號屏蔽字

調用 sigprocmask 函數可以檢測或者設置進程的信號屏蔽字。

#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
返回值:若成功則返回0,若出錯則返回-1

若 oset 參數是一個非空指針,則進程的當前信號屏蔽字將通過 oset 返回。若 set 參數是一個非空指針,則參數 how 將指示如何修改當前信號屏蔽字。how 的可選值如表 1 所示,

表 1: sigprocmask 函數 how 參數可選值
how 說明
SIG_BLOCK 該進程新的信號屏蔽字是其當前信號屏蔽字和 set 指向信號集的並集。
SIG_UNBLOCK 該進程的信號屏蔽字是當前信號屏蔽字和 set 所指向信號集補給的交集。set 包含了我們希望解除阻塞的信號。
SIG_SETMASK 該進程新的信號屏蔽字設置為 set 所指向的信號集。

下面我們來看一個例子。在下面的程序文件中先調用 sigprocmask 設置阻塞信號 SIGALRM,然后調用 alarm(2) 設置一個兩秒鍾的鬧鍾(兩秒鍾之后將向當前進程產生一個 SIGALRM 信號)。在睡眠 4 秒鍾之后(此時應該已經產生了 SIGALRM 信號),調用 sigprocmask 函數解除對信號SIGALRM 的阻塞。

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

static void
sig_alrm(int signo)
{
  printf("received SIGALRM\n");
}

int
main(void)
{
  sigset_t sigset;

  sigemptyset(&sigset);
  sigaddset(&sigset, SIGALRM);
  if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
    printf("sigprocmask error: %s\n", strerror(errno));
    exit(-1);
  }

  if (signal(SIGALRM, sig_alrm) < 0) {
    printf("signal error: %s\n", strerror(errno));
    exit(-1);
  }

  alarm(2);
  sleep(4);
  printf("before unblock sigprocmask\n");
  if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0) {
    printf("sigprocmask SIG_UNBLOCK error: %s\n", strerror(errno));
    exit(-1);
  }
  printf("before exit\n");
  exit(0);
}
sigprocmaskdemo.c

編譯該程序文件 sigprocmaskdemo.c,生成並執行文件 sigprocmaskdemo,

lienhua34:demo$ gcc -o sigprocmaskdemo sigprocmaskdemo.c
lienhua34:demo$ ./sigprocmaskdemo
before unblock sigprocmask
received SIGALRM
before exit

從上面的執行輸出,我們看到信號 SIGALRM 是在調用 sigprocmask函 數 執 行 unblock 之 后 才 被 傳 遞 給 當 前 進 程 進 行 處 理 的。 如 果 我 們 將sigprocmaskdemo.c 中的sigprocemask(SIG_BLOCK, &sigset, NULL) 注釋掉,編譯執行,生成如下結果,

lienhua34:demo$ ./sigprocmaskdemo
received SIGALRM
before unblock sigprocmask
before exit

4 sigpending 獲取進程未決的信號集

函數 sigpending 獲取當前進程所有未決的信號。通過其 set 參數返回未決的信號集。

#include <signal.h>
int sigpending(sigset_t *set);
返回值:若成功則返回0,若出錯則返回-1

下面我們來看一個例子,

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

void
alrm_is_pending(const char *str)
{
  sigset_t pendingsigset;

  printf("%s: ", str);
  if (sigpending(&pendingsigset) < 0) {
    printf("sigpending error: %s\n", strerror(errno));
    exit(-1);
  }
  if (sigismember(&pendingsigset, SIGALRM)) {
    printf("SIGALRM is pending\n");
  } else {
    printf("SIGALRM is not pending\n");
  }
}

int
main(void)
{
  sigset_t sigset;

  sigemptyset(&sigset);
  sigaddset(&sigset, SIGALRM);
  if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
    printf("sigprocmask error: %s\n", strerror(errno));
    exit(-1);
  }
  alrm_is_pending("before alarm");
  alarm(2);
  sleep(4);
  alrm_is_pending("after alarm");
  exit(0);
}
sigpendingdemo.c

編譯該程序 sigpendingdemo.c,生成並執行文件 sigpendingdemo。從下面的運行結果,我們看到調用 alarm 函數產生信號 SIGALRM 之后,該信號在 sigpending 函數的 set 參數指向的信號集中。

lienhua34:demo$ gcc -o sigpendingdemo sigpendingdemo.c
lienhua34:demo$ ./sigpendingdemo
before alarm: SIGALRM is not pending
after alarm: SIGALRM is pending

(done)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM