pthread访问调用信号线程的掩码

Posted tianzeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 值赋给 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 ...
", tid);  
  
  sleep(60);  
  
  printf("[main][%lu] good bye and good luck!
", tid);  
  return 0;  
} 
/* 示例二:主进程创建出来的线程将继承主进程的掩码 
   来源:http://www.leoox.com/?p=321 
   编译:gcc pthread_sigmask2.c -lpthread 
 
    运行后,你可以发现,子线程果然接收不到pthread_kill发送给自己的SIGINT信号, 
    但可以收到SIGUSR1信号!本来要休眠300s,但是收到了SIGUSR1信号, 
    才休眠5秒就(提前294秒)醒来say goodbye了。 
运行结果: 
[[email protected] 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! 
[[email protected] 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
", tid, signo);  
  return;  
}  
  
void* run(void *param)  
{  
  pthread_t tid = pthread_self();  
  printf(">>> Thread[%lu] Running ......
", tid);  
  
  int rc = sleep(300);  
  printf("Thread[%lu] waitup(%d), and say good bye!
", 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 ...
", tid);  
  sleep(1);  
  
  /* 主进程创建出来的线程将继承主进程的掩码。所以子线程收不到SIGINT信号。 */  
  pthread_kill(threads[0], SIGINT);  
  printf("[main][%lu] send signal SIGINT ...
", tid);  
  sleep(5);  
  
  /* 子线程可以收到SIGUSR1信号。 */  
  pthread_kill(threads[0], SIGUSR1);  
  
  pthread_join(threads[0], NULL);  
  
  sleep(1);  
  printf("[main][%lu] good bye and good luck!
", tid);  
  return 0;  
}  
/* 示例三:子线程可以后天培养自己对信号的喜好 
   来源:http://www.leoox.com/?p=321 
   编译:gcc pthread_sigmask3.c -lpthread 
 
子线程天生继承了主线程对信号的喜好,但是自己可以通过后天的努力改变。 
比如主线程喜欢SIGUSR1信号,但是子线程可以不喜欢它,屏蔽掉SIGUSR1信号。 
 
由此可见,linux里的每个线程有自己的信号掩码,所以使用pthread_kill给指定线程发送信号时, 
一定谨慎设置好线程的信号掩码。 
 
当然,用kill发送信号,在多线程环境下,kill所产生的信号时传递到整个进程的, 
并且所有线程都有机会收到这个信号,但具体是哪个线程处理这个信号,就不一定。 
一般情况下,都是主线程处理这个信号。 
 
     
运行结果: 
[[email protected] thread]$ gcc pthread_sigmask3.c -lpthread 
[[email protected] 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! 
[[email protected] 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 ...
", time(NULL), tid, signo);  
  sleep(20);  
  printf("[%u]Thread[%lu] catch signo = %d ... done
", 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 ......
", time(NULL), tid);  
  
  int rc = sleep(300);  
  
  printf("[%u]Thread[%lu] waitup(%d), and say good bye!
", 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 ...
", tid);  
  sleep(5);  
  
  /* 子线程屏蔽了SIGUSR1信号,所以子线程收不到SIGUSR1信号。 */  
  pthread_kill(threads[0], SIGUSR1);  
  printf("[main][%lu] send signal SIGUSR1 ...
", tid);  
  sleep(5);  
  
  pthread_join(threads[0], NULL);  
  sleep(1);  
  
  printf("[main][%lu] good bye and good luck!
", tid);  
  return 0;  
}  
/* 示例四:主线程收到信号,没处理完,又来一个信号,子线程会处理吗? 会! 
   来源:http://www.leoox.com/?p=321 
   编译:gcc pthread_sigmask4.c -lpthread 
 
   在一个命令行终端启动a.out,在另一个命令行终端发送4次SIGUSR1信号给a.out进程 
     
运行结果: 
[[email protected] 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! 
[[email protected] 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 ...
", time(NULL), tid, signo);  
  /* 
   * 信号处理函数休眠2秒,这期间再发送同一个信号,观察子线程的表现。 
   */  
  sleep(2);  
  printf("[%u]Thread[%lu] catch signo = %d ... done
", time(NULL), tid, signo);  
  return;  
}  
  
void* run(void *param)  
{  
  pthread_t tid = pthread_self();  
  printf(">>> [%u]Thread[%lu] Running ......
", time(NULL), tid);  
  
  int rc = sleep(60);  
  
  printf(">>> [%u]Thread[%lu] waitup(%d), and say good bye!
", 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 ...
", tid);  
  
  pthread_join(threads[0], NULL);  
  sleep(1);  
  
  printf("[main][%lu] good bye and good luck!
", tid);  
  return 0;  
}  


如果把void handler(int signo)函数中的sleep(2)注释掉,运行结果如下:

[[email protected] 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!
[[email protected] thread]$

可见,若子线程“没空”(在sleep),主线程有空,信号都给主线程处理了。

 

pthread_sigmask 返回值

pthread_sigmask() 在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下情况,pthread_sigmask() 将失败并返回相应的值。

EINVAL

描述:

未定义 how 的值。

 























以上是关于pthread访问调用信号线程的掩码的主要内容,如果未能解决你的问题,请参考以下文章

在信号句柄中使用 pthread_cond_wait 的 Pthread 被阻塞

为啥我无法使用 CAGradientLayer 的掩码

10.12 sigprocmask函数

10.12 sigprocmask函数

使用pthread时,字节仍可访问

无法获得所需的掩码检测输出