1. 程式人生 > >pthread訪問調用信號線程的掩碼

pthread訪問調用信號線程的掩碼

flag fill tid code HR threads TE argc argv

pthread_sigmask 語法

int	pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
#include <pthread.h>

#include <signal.h>
int ret;

sigset_t old, new;

ret = pthread_sigmask(SIG_SETMASK, &new, &old); /* set new mask */

ret = pthread_sigmask(SIG_BLOCK, &new, &old); /* blocking mask */

ret
= pthread_sigmask(SIG_UNBLOCK, &new, &old); /* unblocking */

how 用來確定如何更改信號組。how 可以為以下值之一:

  • SIG_BLOCK。向當前的信號掩碼中添加 new,其中 new 表示要阻塞的信號組。

  • SIG_UNBLOCK。從當前的信號掩碼中刪除 new,其中 new 表示要取消阻塞的信號組。

  • SIG_SETMASK。將當前的信號掩碼替換為 new,其中 new 表示新的信號掩碼。

new 的值為 NULL 時,how 的值沒有意義,線程的信號掩碼不發生變化。要查詢當前已阻塞的信號,請將 NULL

值賦給 new 參數。

除非 old 變量為 NULL,否則 old 指向用來存儲以前的信號掩碼的空間。

Linux內核中有一個專門的函數集合來執行設置和修改信號掩碼,它們放在kernel/signal.c中,其函數形式和功能如下:

int sigemptyset(sigset_t *mask) 清所有信號掩碼的阻塞標誌

int sigfillset(sigset_t *mask, int signum) 設置所有信號掩碼的阻塞標誌

int sigdelset(sigset_t *mask, int signum) 刪除個別信號阻塞

int sigaddset(sigset_t *mask, int signum) 增加個別信號阻塞

int sigisnumber(sigset_t *mask, int signum) 確定特定的信號是否在掩碼中被標誌為阻塞。

另外,進程也可以利用sigprocmask()系統調用改變和檢查自己的信號掩碼的值,其實現代碼在kernel/signal.c中,原型為:

int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)

/* 示例一:屏蔽信號SIGINT 
   來源:http://www.leoox.com/?p=321 
   編譯:gcc pthread_sigmask1.c -lpthread 
 
   運行後,你發現你按下CTRL+C(觸發SIGINT信號),這個程序根本停不下來。因為SIGINT信號已經如我們所願被屏蔽掉了。 
*/  
  
#include <pthread.h>  
#include <stdio.h>  
#include <sys/signal.h>  
#include <string.h>  
  
int main(int argc, char** argv)  
{  
  pthread_t tid = pthread_self();  
  
  sigset_t mask;  
  sigemptyset(&mask);  
  sigaddset(&mask, SIGINT);  
  pthread_sigmask(SIG_SETMASK, &mask, NULL);//SIG_BLOCK SIG_SETMASK 會屏蔽掉SIGINT,但SIG_UNBLOCK不會屏蔽SIGINT  
  
  printf("[main][%lu] working hard ...\n", tid);  
  
  sleep(60);  
  
  printf("[main][%lu] good bye and good luck!\n", tid);  
  return 0;  
} 
/* 示例二:主進程創建出來的線程將繼承主進程的掩碼 
   來源:http://www.leoox.com/?p=321 
   編譯:gcc pthread_sigmask2.c -lpthread 
 
    運行後,你可以發現,子線程果然接收不到pthread_kill發送給自己的SIGINT信號, 
    但可以收到SIGUSR1信號!本來要休眠300s,但是收到了SIGUSR1信號, 
    才休眠5秒就(提前294秒)醒來say goodbye了。 
運行結果: 
[lgh@lghvm001 thread]$ ./a.out  
[main][139999919077120] working hard ... 
>>> Thread[139999919068928] Running ...... 
[main][139999919077120] send signal SIGINT ... 
Thread[139999919068928] catch signo = 10 
Thread[139999919068928] waitup(294), and say good bye! 
[main][139999919077120] good bye and good luck! 
[lgh@lghvm001 thread]$  
 
*/  
  
  
  
#include <pthread.h>  
#include <stdio.h>  
#include <sys/signal.h>  
#include <string.h>  
  
void handler(int signo)  
{  
  pthread_t tid = pthread_self();  
  printf("Thread[%lu] catch signo = %d\n", tid, signo);  
  return;  
}  
  
void* run(void *param)  
{  
  pthread_t tid = pthread_self();  
  printf(">>> Thread[%lu] Running ......\n", tid);  
  
  int rc = sleep(300);  
  printf("Thread[%lu] waitup(%d), and say good bye!\n", tid, rc);  
  return NULL;  
}  
  
int main(int argc, char** argv)  
{  
  int ret = 0, i = 0;  
  pthread_t tid = pthread_self();  
    
  /* 註冊SIGUSR1信號處理函數 */  
  struct sigaction sa;  
  memset(&sa, 0, sizeof(sa));  
  sigemptyset(&sa.sa_mask);  
  sa.sa_flags = 0;  
  sa.sa_handler = handler;  
  sigaction(SIGUSR1, &sa, NULL);  
  
  /* 屏蔽信號SIGINT */  
  sigset_t mask;  
  sigemptyset(&mask);  
  sigaddset(&mask, SIGINT);  
  pthread_sigmask(SIG_BLOCK, &mask, NULL);  
  
  pthread_t threads[2];  
  pthread_create(&threads[0], NULL, run, NULL);  
  printf("[main][%lu] working hard ...\n", tid);  
  sleep(1);  
  
  /* 主進程創建出來的線程將繼承主進程的掩碼。所以子線程收不到SIGINT信號。 */  
  pthread_kill(threads[0], SIGINT);  
  printf("[main][%lu] send signal SIGINT ...\n", tid);  
  sleep(5);  
  
  /* 子線程可以收到SIGUSR1信號。 */  
  pthread_kill(threads[0], SIGUSR1);  
  
  pthread_join(threads[0], NULL);  
  
  sleep(1);  
  printf("[main][%lu] good bye and good luck!\n", tid);  
  return 0;  
}  
/* 示例三:子線程可以後天培養自己對信號的喜好 
   來源:http://www.leoox.com/?p=321 
   編譯:gcc pthread_sigmask3.c -lpthread 
 
子線程天生繼承了主線程對信號的喜好,但是自己可以通過後天的努力改變。 
比如主線程喜歡SIGUSR1信號,但是子線程可以不喜歡它,屏蔽掉SIGUSR1信號。 
 
由此可見,linux裏的每個線程有自己的信號掩碼,所以使用pthread_kill給指定線程發送信號時, 
一定謹慎設置好線程的信號掩碼。 
 
當然,用kill發送信號,在多線程環境下,kill所產生的信號時傳遞到整個進程的, 
並且所有線程都有機會收到這個信號,但具體是哪個線程處理這個信號,就不一定。 
一般情況下,都是主線程處理這個信號。 
 
     
運行結果: 
[lgh@lghvm001 thread]$ gcc pthread_sigmask3.c -lpthread 
[lgh@lghvm001 thread]$ ./a.out  
[main][140613382825728] working hard ... 
>>> [1481543657]Thread[140613382817536] Running ...... 
[main][140613382825728] send signal SIGUSR1 ... 
 
[1481543841]Thread[140613382825728] catch signo = 10 ...        //子線程sleep期間,kill -SIGUSR1 2839 
[1481543861]Thread[140613382825728] catch signo = 10 ... done 
 
[1481543957]Thread[140613382817536] waitup(0), and say good bye! 
[main][140613382825728] good bye and good luck! 
[lgh@lghvm001 thread]$ 
 
*/  
  
  
#include <pthread.h>  
#include <stdio.h>  
#include <sys/signal.h>  
#include <string.h>  
  
void handler(int signo)  
{  
  pthread_t tid = pthread_self();  
  printf("[%u]Thread[%lu] catch signo = %d ...\n", time(NULL), tid, signo);  
  sleep(20);  
  printf("[%u]Thread[%lu] catch signo = %d ... done\n", time(NULL), tid, signo);  
  return;  
}  
  
void* run(void *param)  
{  
  pthread_t tid = pthread_self();  
  sigset_t mask;  
#if 1  
  /* 這種情況下,本線程屏蔽所有的信號 */  
  sigfillset(&mask);  
#endif  
  
#if 0  
  /* 這種情況下,本線程不屏蔽任何信號 */  
  sigemptyset(&mask);  
#endif  
  
#if 0   
  /* 這種情況,本線程屏蔽以下的指定信號 */  
  sigemptyset(&mask);  
  sigaddset(&mask, SIGINT);  
  sigaddset(&mask, SIGQUIT);  
  sigaddset(&mask, SIGHUP);  
  sigaddset(&mask, SIGTERM);  
#endif   
  
  pthread_sigmask(SIG_SETMASK, &mask, NULL);  
    
  printf(">>> [%u]Thread[%lu] Running ......\n", time(NULL), tid);  
  
  int rc = sleep(300);  
  
  printf("[%u]Thread[%lu] waitup(%d), and say good bye!\n", time(NULL), tid, rc);  
  return NULL;  
}  
  
int main(int argc, char** argv)  
{  
  pthread_t tid = pthread_self();  
    
  /* 註冊SIGUSR1信號處理函數 */  
  struct sigaction sa;  
  memset(&sa, 0, sizeof(sa));  
  sigemptyset(&sa.sa_mask);  
  sa.sa_flags = 0;  
  sa.sa_handler = handler;  
  sigaction(SIGUSR1, &sa, NULL);  
  
  pthread_t threads[1];  
  pthread_create(&threads[0], NULL, run, NULL);  
  printf("[main][%lu] working hard ...\n", tid);  
  sleep(5);  
  
  /* 子線程屏蔽了SIGUSR1信號,所以子線程收不到SIGUSR1信號。 */  
  pthread_kill(threads[0], SIGUSR1);  
  printf("[main][%lu] send signal SIGUSR1 ...\n", tid);  
  sleep(5);  
  
  pthread_join(threads[0], NULL);  
  sleep(1);  
  
  printf("[main][%lu] good bye and good luck!\n", tid);  
  return 0;  
}  
/* 示例四:主線程收到信號,沒處理完,又來一個信號,子線程會處理嗎? 會! 
   來源:http://www.leoox.com/?p=321 
   編譯:gcc pthread_sigmask4.c -lpthread 
 
   在一個命令行終端啟動a.out,在另一個命令行終端發送4次SIGUSR1信號給a.out進程 
     
運行結果: 
[lgh@lghvm001 thread]$ ./a.out  
[main][139796483913472] working hard ... 
>>> [1481545031]Thread[139796483905280] Running ...... //此時發送四次kill -SIGUSR1 2886,2886為a.out的進程號 
[1481545054]Thread[139796483913472] catch signo = 10 ... 
[1481545055]Thread[139796483905280] catch signo = 10 ... 
[1481545056]Thread[139796483913472] catch signo = 10 ... done 
[1481545056]Thread[139796483913472] catch signo = 10 ... 
[1481545057]Thread[139796483905280] catch signo = 10 ... done 
[1481545057]Thread[139796483905280] catch signo = 10 ... 
[1481545058]Thread[139796483913472] catch signo = 10 ... done 
[1481545059]Thread[139796483905280] catch signo = 10 ... done 
>>> [1481545059]Thread[139796483905280] waitup(35), and say good bye! 
[main][139796483913472] good bye and good luck! 
[lgh@lghvm001 thread]$  
 
*/  
  
  
#include <pthread.h>  
#include <stdio.h>  
#include <sys/signal.h>  
#include <string.h>  
  
void handler(int signo)  
{  
  pthread_t tid = pthread_self();  
  printf("[%u]Thread[%lu] catch signo = %d ...\n", time(NULL), tid, signo);  
  /* 
   * 信號處理函數休眠2秒,這期間再發送同一個信號,觀察子線程的表現。 
   */  
  sleep(2);  
  printf("[%u]Thread[%lu] catch signo = %d ... done\n", time(NULL), tid, signo);  
  return;  
}  
  
void* run(void *param)  
{  
  pthread_t tid = pthread_self();  
  printf(">>> [%u]Thread[%lu] Running ......\n", time(NULL), tid);  
  
  int rc = sleep(60);  
  
  printf(">>> [%u]Thread[%lu] waitup(%d), and say good bye!\n", time(NULL), tid, rc);  
  return NULL;  
}  
  
int main(int argc, char** argv)  
{  
  pthread_t tid = pthread_self();  
    
  /* 註冊SIGUSR1信號處理函數 */  
  struct sigaction sa;  
  memset(&sa, 0, sizeof(sa));  
  sigemptyset(&sa.sa_mask);  
  sa.sa_flags = 0;  
  sa.sa_handler = handler;  
  sigaction(SIGUSR1, &sa, NULL);  
  
  pthread_t threads[1];  
  pthread_create(&threads[0], NULL, run, NULL);  
  printf("[main][%lu] working hard ...\n", tid);  
  
  pthread_join(threads[0], NULL);  
  sleep(1);  
  
  printf("[main][%lu] good bye and good luck!\n", tid);  
  return 0;  
}  


如果把void handler(int signo)函數中的sleep(2)註釋掉,運行結果如下:

[lgh@lghvm001 thread]$ ./a.out
[main][140353429055232] working hard ...
>>> [1481596389]Thread[140353429047040] Running ...... //此處發送6次“kill -SIGUSR1 3123”
[1481596401]Thread[140353429055232] catch signo = 10 ...
[1481596401]Thread[140353429055232] catch signo = 10 ... done
[1481596401]Thread[140353429055232] catch signo = 10 ...
[1481596401]Thread[140353429055232] catch signo = 10 ... done
[1481596402]Thread[140353429055232] catch signo = 10 ...
[1481596402]Thread[140353429055232] catch signo = 10 ... done
[1481596402]Thread[140353429055232] catch signo = 10 ...
[1481596402]Thread[140353429055232] catch signo = 10 ... done
[1481596402]Thread[140353429055232] catch signo = 10 ...
[1481596402]Thread[140353429055232] catch signo = 10 ... done
[1481596403]Thread[140353429055232] catch signo = 10 ...
[1481596403]Thread[140353429055232] catch signo = 10 ... done
>>> [1481596449]Thread[140353429047040] waitup(0), and say good bye!
[main][140353429055232] good bye and good luck!
[lgh@lghvm001 thread]$

可見,若子線程“沒空”(在sleep),主線程有空,信號都給主線程處理了。

 

pthread_sigmask 返回值

pthread_sigmask() 在成功完成之後返回零。其他任何返回值都表示出現了錯誤。如果出現以下情況,pthread_sigmask() 將失敗並返回相應的值。

EINVAL

描述:

未定義 how 的值。

pthread訪問調用信號線程的掩碼