pthread訪問調用信號線程的掩碼
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
除非 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訪問調用信號線程的掩碼