Linux 上 C 中的多线程信号处理
Multi Threaded Signal Handling in C on Linux
广泛的问题:我的代码有什么问题,以至于生成的所有信号都没有被两个处理程序线程捕获?
我的可怜问题的不幸细节:我应该编写一些代码,其中包含一个主函数、3 个生成器线程以生成 sig1 和 sig2 类型的信号以及两个信号处理线程。我尝试使用下面显示的代码解决此问题,但我 运行 遇到了一些错误。我尝试将 sigaction 与 sigwaitinfo 和 sigwait 一起使用来捕获信号。但是这两种方法似乎都不能正常工作。在附加的代码中,handler1 使用 sigaction 和 sigwaitinfo,handler2 使用 sigwait。但是我已经尝试让两个处理程序都使用其中一个,但我的结果从来没有像我认为的那样。似乎有些信号从未被捕捉到。我的代码有什么问题以至于所有的信号都没有被捕获?这是一个示例输出
示例输出
收到信号 1
收到信号 2
收到信号 1
收到信号 2
收到信号 2
sigSent1==2,sigSent2==7,sigReceived1==2,sigReceived2==3
期望的输出是
可能的期望输出
收到信号 1
收到信号 2
收到信号 1
收到信号 2
收到信号 2
收到信号 1
收到信号 2
收到信号 1
收到信号 2
sigSent1==4,sigSent2==5,sigReceived1==4,sigReceived2==5
抱歉,如果这个问题问得太多了,但我真的不知道为什么没有捕捉到所有信号,并且一直在谷歌上搜索并测试这个问题,今天和昨天分别测试了 6 个小时和 3 个小时,同时还看着那个人页...我可能遗漏了一些明显的东西...
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>
/*
Pre-definitions of functions
*/
void generator();
void handler1();
void handler2();
void reporter();
/*
Global Variables
*/
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;
int receivedSignal1=0;
int receivedSignal2=0;
sem_t s_lock;
sem_t r_lock;
sigset_t set;
pthread_mutex_t lock;
pthread_t tid[5];
/*
Main function
*/
int main(int argc, char ** argv)
{
int i=0;
int randomNum=0;
int error;
int pid;
sigset_t mask_all,mask_one,prev_one;
//Setting up signals
//Get Random time
time_t now;
time(&now);
//semaphore is initialized to be global and val 1
sem_init(&s_lock,0,1);
sem_init(&r_lock,0,1);
srand((unsigned) time(&now));
//Blakc in main thread
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGUSR2);
pthread_sigmask(SIG_BLOCK,&set,NULL);
pthread_sigmask(SIG_BLOCK,&set,NULL);
//Loops until more threads created than 2
while(i<3)
{ error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
i++;
}//end while loop
while(i<5)
{
error=pthread_create(&tid[3],NULL,(void*)handler1,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
error=pthread_create(&tid[4],NULL,(void*)handler2,NULL);
if(error!=0)
{
printf("failed to create thread \n");
}
i++;
}
//join the threads so main won't return
i=0;
int returnVal;
sleep(10);
printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
while(i<5)//Loops until threads are joined
{
// printf("gonna join %d\n",i);
pthread_join(tid[i],NULL);
/*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
{
printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
}*/
i++;
}//end while
return 0;
}//end of main function
/*
Generator threads
*/
void generator()
{
sleep(1);
int i=3;
int randomNum=0;
int val=0;
int total_signal_c=9997;
while(total_signal_c<10000)
{
usleep(1);
//Randomly select to generate SIGUSR1 or SIGUSR2
//Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
// printf("total_signal_count%d\n",total_signal_c);
//Create either a sig1 signal or sig2 signal
randomNum=rand()%2;
switch(randomNum)
{
case 0:
val=pthread_kill(tid[3],SIGUSR1);
if(val!=0)
{
printf("kill fail ==%d\n",val);
}
sem_wait(&s_lock);
//semaphore
//mutex
sentSignal1++;
sem_post(&s_lock);
break;
case 1:
val=pthread_kill(tid[4],SIGUSR2);
if(val!=0)
{
printf("kill fail2\n");
}
sem_wait(&s_lock);
sentSignal2++;
sem_post(&s_lock);
//
//
break;
}
i++;
total_signal_c++;
//delay for a random time, 0.01 to 0.1 second
}
}
/*
Handler 1 threads
*/
void handler1()
{
//Setting up signals
// printf("In handler1\n");
struct sigaction s;
siginfo_t info;
sigemptyset(&s.sa_mask);
//use signal to perma block for handler2
signal(SIGUSR2,handler1);
//Add Sigusr1 to set
sigaddset((&s.sa_mask),SIGUSR1);
pthread_sigmask(SIG_BLOCK,&s.sa_mask,NULL);
int val=-1;
//use signal(), sigaddset(), pthread_sigmask() etc to block and unblock signals as required.
while(1)
{ //use sigwaitinfo(); to receive a signal
val=-1;
val=sigwaitinfo(&s.sa_mask,&info);
//if signal received modify the corresponding counter
if(info.si_signo==SIGUSR1){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal1++;
//decrement semaphore lock
sem_post(&r_lock);
printf("signal 1 received\n");
}
if(val==-1)
{
// break;
}
}
pthread_exit(NULL);
}
/*
Handler2 threads
*/
void handler2()
{
int sigInfo=0;
//use signal to perma block for handler2
signal(SIGUSR1,handler2);
int val=-1;
while(1)
{ //use sigwaitinfo(); to receive a signal
val=-1;
val=sigwait(&set,&sigInfo);
//if signal received modify the corresponding counter
if(sigInfo==SIGUSR2){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal2++;
//decrement semaphore lock
sem_post(&r_lock);
printf("signal 2 received\n");
}
}
pthread_exit(NULL);
}
当存在具有相同代码的未决信号时,一些信号可能会丢失。来自 sigaction 的规范:
If a subsequent occurrence of a pending signal is generated, it is implementation-dependent as to whether the signal is delivered or accepted more than once in circumstances other than those in which queueing is required under the Realtime Signals Extension option. The order in which multiple, simultaneously pending signals outside the range SIGRTMIN to SIGRTMAX are delivered to or accepted by a process is unspecified.
如果你想捕获所有的信号,你有两个解决方案:
- 使用值从 SIGRTMIN 到 SIGRTMAX 的实时信号,而不是 SIGUSR1 和 SIGUSR2。
pthread_sigqueue()
和 pthread_kill()
都将无法发送信号,如果 SIGQUEUE_MAX
信号未决或者系统没有足够的资源来排队信号。
- 等待先行信号被捕捉到再发送一个。
编辑:
1。一些解释来回答你最后的评论。
您不能使用 signal()
仅阻塞信号,您可以忽略它(使用 SIG_IGN 而不是处理函数)或注册处理函数。有了处理函数,我想我们可以说信号被阻塞并被捕获。
我认为你的t.a。希望您使用 signal()
和处理函数处理一种类型的信号,例如 SIGUSR1,并使用 sigwaitinfo()
.
通过线程处理 SIGUSR2
使用signal()
你不需要阻塞你想要捕获的信号,它可以在主线程中完成。
使用 sigwaitinfo()
你需要至少在接收它的线程中阻塞你想要捕获的信号。
你可以看看我贴在最后的源代码post。
2。更精确。
要在不放置自动 catch/handler 函数的情况下阻止信号,您必须在单线程程序中使用 sigprocmask()
,或在多线程程序中使用 pthread_sigmask()
。您还可以使用 sigaction()
以在执行信号处理函数期间阻止一些传入信号。
关于信号捕捉,有两种捕捉信号的方法:
信号处理函数在signal()
(或sigaction()
)注册,并在接收到信号时自动调用,除非信号在所有线程中被阻塞。为了使 signal()
工作,您必须至少让一个线程不阻塞信号。您不必使用 sigwait()
来处理信号,因为程序会在执行的同时自动等待。
使用 signal()
将在接收到信号时创建信号上下文,您将必须在信号处理函数中使用异步信号安全函数。 signal()
为整个进程注册一个处理函数,而不仅仅是为调用线程。
处理线程需要使用 sigwait()
或 sigwaitinfo()
捕获信号,并且这些线程不限于异步信号安全函数。必须至少在作为 pthread_kill()
.
目标的线程中使用 pthread_sigmask()
阻塞要捕获的信号
并且必须在所有线程中被阻塞,以便捕获进程范围内的信号,例如由 kill()
触发的信号(如果至少一个线程没有阻塞信号,那么它将对过程)。
3。关于您的程序正在做什么的一些解释。
在主线程中,信号SIGUSR1和SIGUSR2被阻塞了,所以在这个阻塞之后主线程创建的所有线程都会阻塞这些信号,因为它们继承了创建掩码线程。
当您调用 signal()
时,它会将函数 handler1()
和 handler2()
注册为信号处理函数,以便在线程接收到信号时调用。但是这些信号对于所有线程都是阻塞的,所以 handler1()
和 handler2()
不会被调用为信号处理函数。所以,在你的程序中使用signal()
是没有用的。
此外,handler1()
和 handler2()
旨在处理线程,而不是信号处理函数。所以你不应该用signal()
注册它们,你必须注册非线程函数。
您应该仅在 pthread_kill()
没有失败时才增加发送信号的计数器。
创建处理线程时,程序创建了2个无用线程,因为循环是针对i = 3
和i = 4
执行的,而你在这个循环中创建了2个线程。所以正确的代码是while(i < 4)
,或者最好去掉循环。
4。我修改了你的程序,以便使用 signal()
:
捕获 SIGUSR1
你会看到它只需要在handler2_thread()
中屏蔽SIGUSR2。程序中不需要其他阻塞。
在这段代码中,您将看到处理线程和信号处理函数之间的区别,thread1
接收到的信号由信号处理函数handler1_func()
处理,而 handler2_thread
接收的信号在线程本身中处理。
变量 receivedSignal1_flag
被声明为 volatile
并且类型为 sig_atomic_t
因为在检查和重置它的线程与将其设置为 1
的处理函数。使用这种方式,一些捕捉到的信号将不会被计算在内。关于我在 sig_atomic_t
上读到的内容,我不确定是否可以在 handler1_func()
中直接增加计数器 receivedSignal1
,因为增加操作不是原子的,所以可以被另一个信号处理程序打扰。但是如果 handler_func()
是唯一一个读取和写入 receivedSignal1
并声明它 volatile
和 sig_atomic_t
的信号处理程序,也许这是可能的。另请注意,receivedSignal1_flag
未被信号量或互斥锁锁定,因为只有一个线程在使用它。
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>
/*
Pre-definitions of functions
*/
void generator();
void handler1_func(int);
void thread1();
void handler2_thread();
void reporter();
/*
Global Variables
*/
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;
///////////////////////////////////////
//
// receivedSignal1_flag is volatile and
// sig_atomic_t because there is a race
// condition on it (used in the signal
// handler, and in the thread).
//
///////////////////////////////////////
volatile sig_atomic_t receivedSignal1_flag;
int receivedSignal1=0;
int receivedSignal2=0;
sem_t s_lock;
sem_t r_lock;
pthread_mutex_t lock;
pthread_t tid[5];
/*
Main function
*/
int main(int argc, char ** argv)
{
int i=0;
int randomNum=0;
int error;
int pid;
sigset_t mask_all,mask_one,prev_one;
//Setting up signals
//Get Random time
time_t now;
time(&now);
//semaphore is initialized to be global and val 1
sem_init(&s_lock,0,1);
sem_init(&r_lock,0,1);
srand((unsigned) time(&now));
//Loops until more threads created than 2
while(i<3)
{ error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
i++;
}//end while loop
error=pthread_create(&tid[3],NULL,(void*)thread1,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
error=pthread_create(&tid[4],NULL,(void*)handler2_thread,NULL);
if(error!=0)
{
printf("failed to create thread \n");
}
//join the threads so main won't return
i=0;
int returnVal;
sleep(15);
printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
while(i<5)//Loops until threads are joined
{
// printf("gonna join %d\n",i);
pthread_join(tid[i],NULL);
/*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
{
printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
}*/
i++;
}//end while
return 0;
}//end of main function
/*
Generator threads
*/
void generator()
{
sleep(5);
int i=3;
int randomNum=0;
int val=0;
int total_signal_c=9990;
while(total_signal_c<10000)
{
usleep(1);
//Randomly select to generate SIGUSR1 or SIGUSR2
//Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
// printf("total_signal_count%d\n",total_signal_c);
//Create either a sig1 signal or sig2 signal
randomNum=rand()%2;
switch(randomNum)
{
case 0:
/////////////////////////////////////////
// Send SIGUSR1 to thread1
/////////////////////////////////////////
val=pthread_kill(tid[3],SIGUSR1);
if(val!=0)
{
printf("\nkill fail ==%d",val);
} else {
sem_wait(&s_lock);
//semaphore
//mutex
sentSignal1++;
sem_post(&s_lock);
}
break;
case 1:
/////////////////////////////////////////
// Send SIGUSR2 to handler2_thread
/////////////////////////////////////////
val=pthread_kill(tid[4],SIGUSR2);
if(val!=0)
{
printf("\nkill fail2");
} else {
sem_wait(&s_lock);
sentSignal2++;
sem_post(&s_lock);
//
//
}
break;
}
i++;
total_signal_c++;
//delay for a random time, 0.01 to 0.1 second
}
}
//////////////////////////////////////////
//
// Signal handler function for SIGUSR1:
//
//////////////////////////////////////////
void handler1_func(int signo)
{
// write on stdout using an async-signal-safe function:
write(STDOUT_FILENO,"\nSignal handler function: SIGUSR1 caught\n",41);
// set the received signal flag to 1:
if(signo == SIGUSR1) receivedSignal1_flag = 1;
}
/////////////////////////////////////////////////////////////
//
// The thread that will receive SIGUSR1 but not handle it
// because handler1_func() will handle it automatically:
//
/////////////////////////////////////////////////////////////
void thread1()
{
//////////////////////////////////////////////
//
// register handler1_func() as signal handler
// for the whole process, not only the thread.
// It means that if another thread doesn't
// block SIGUSR1 and receive it, then
// handler1_func() will also be called:
//
//////////////////////////////////////////////
signal(SIGUSR1,handler1_func);
while(1)
{
///////////////////////////////////////////////////
// If a signal has been handled by handler1_func()
// then receivedSignal1_flag = 1.
// And so increment receivedSignal1 and print.
///////////////////////////////////////////////////
if(receivedSignal1_flag == 1) {
// reset the flag:
receivedSignal1_flag = 0;
sem_wait(&r_lock);
receivedSignal1++;
printf("\nThread1: SIGUSR1 received and handled by handler1_func()\n");
sem_post(&r_lock);
}
}
pthread_exit(NULL);
}
////////////////////////////////////////
//
// Handling thread for SIGUSR2:
//
////////////////////////////////////////
void handler2_thread()
{
///////////////////////////////////////////////
//
// Need to block SIGUSR2 in order to avoid
// the default handler to be called.
//
///////////////////////////////////////////////
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGUSR2);
pthread_sigmask(SIG_BLOCK,&set,NULL);
siginfo_t info;
int val=-1;
while(1)
{
val=-1;
val=sigwaitinfo(&set,&info);
//if signal received modify the corresponding counter
if(info.si_signo==SIGUSR2){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal2++;
//decrement semaphore lock
printf("\nhandler2_thread: signal 2 received\n");
sem_post(&r_lock);
}
}
pthread_exit(NULL);
}
只能从信号处理程序中安全地调用异步信号安全函数。 sigwait()
和 sigwaitinfo()
不是 异步信号安全的。请参阅 2.4 信号概念 ,位于 http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html. Also see the Linux signal.7
man page。 printf()
异步信号也不安全。
在信号处理程序中调用 pthread_exit()
是未定义的行为。它将终止线程 - 但在信号处理上下文中,可能会导致重大问题。以下问题刚刚开始涉及在信号处理程序中调用 pthread_exit()
导致的问题:pthread_exit() in signal handler and How to properly terminate a thread in a signal handler? See also http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html
从根本上说,您的代码很混乱。您将 handler1()
和 handler2()
作为单独的线程启动,然后将这些相同的函数注册为信号处理程序,然后在函数中调用 sigwait()
/sigwaitinfo()
。
鉴于代码结合线程、信号处理程序、while(1)...
循环的方式,几乎不可能开始猜测发生了什么。例如,您可能会得到生成卡在无限循环中的信号处理程序的线程。
这行代码:
signal(SIGUSR1,handler2);
表示当收到 SIGUSR1
时,将在信号上下文中调用 handler2()
- 但是 handler2()
中有一个 while(1)
循环...
异步信号处理是一个很难掌握的概念。我会说你需要从一些比多个线程试图相互发送信号更简单的事情开始。
广泛的问题:我的代码有什么问题,以至于生成的所有信号都没有被两个处理程序线程捕获?
我的可怜问题的不幸细节:我应该编写一些代码,其中包含一个主函数、3 个生成器线程以生成 sig1 和 sig2 类型的信号以及两个信号处理线程。我尝试使用下面显示的代码解决此问题,但我 运行 遇到了一些错误。我尝试将 sigaction 与 sigwaitinfo 和 sigwait 一起使用来捕获信号。但是这两种方法似乎都不能正常工作。在附加的代码中,handler1 使用 sigaction 和 sigwaitinfo,handler2 使用 sigwait。但是我已经尝试让两个处理程序都使用其中一个,但我的结果从来没有像我认为的那样。似乎有些信号从未被捕捉到。我的代码有什么问题以至于所有的信号都没有被捕获?这是一个示例输出
示例输出
收到信号 1
收到信号 2
收到信号 1
收到信号 2
收到信号 2
sigSent1==2,sigSent2==7,sigReceived1==2,sigReceived2==3
期望的输出是
可能的期望输出
收到信号 1
收到信号 2
收到信号 1
收到信号 2
收到信号 2
收到信号 1
收到信号 2
收到信号 1
收到信号 2
sigSent1==4,sigSent2==5,sigReceived1==4,sigReceived2==5
抱歉,如果这个问题问得太多了,但我真的不知道为什么没有捕捉到所有信号,并且一直在谷歌上搜索并测试这个问题,今天和昨天分别测试了 6 个小时和 3 个小时,同时还看着那个人页...我可能遗漏了一些明显的东西...
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>
/*
Pre-definitions of functions
*/
void generator();
void handler1();
void handler2();
void reporter();
/*
Global Variables
*/
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;
int receivedSignal1=0;
int receivedSignal2=0;
sem_t s_lock;
sem_t r_lock;
sigset_t set;
pthread_mutex_t lock;
pthread_t tid[5];
/*
Main function
*/
int main(int argc, char ** argv)
{
int i=0;
int randomNum=0;
int error;
int pid;
sigset_t mask_all,mask_one,prev_one;
//Setting up signals
//Get Random time
time_t now;
time(&now);
//semaphore is initialized to be global and val 1
sem_init(&s_lock,0,1);
sem_init(&r_lock,0,1);
srand((unsigned) time(&now));
//Blakc in main thread
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGUSR2);
pthread_sigmask(SIG_BLOCK,&set,NULL);
pthread_sigmask(SIG_BLOCK,&set,NULL);
//Loops until more threads created than 2
while(i<3)
{ error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
i++;
}//end while loop
while(i<5)
{
error=pthread_create(&tid[3],NULL,(void*)handler1,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
error=pthread_create(&tid[4],NULL,(void*)handler2,NULL);
if(error!=0)
{
printf("failed to create thread \n");
}
i++;
}
//join the threads so main won't return
i=0;
int returnVal;
sleep(10);
printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
while(i<5)//Loops until threads are joined
{
// printf("gonna join %d\n",i);
pthread_join(tid[i],NULL);
/*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
{
printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
}*/
i++;
}//end while
return 0;
}//end of main function
/*
Generator threads
*/
void generator()
{
sleep(1);
int i=3;
int randomNum=0;
int val=0;
int total_signal_c=9997;
while(total_signal_c<10000)
{
usleep(1);
//Randomly select to generate SIGUSR1 or SIGUSR2
//Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
// printf("total_signal_count%d\n",total_signal_c);
//Create either a sig1 signal or sig2 signal
randomNum=rand()%2;
switch(randomNum)
{
case 0:
val=pthread_kill(tid[3],SIGUSR1);
if(val!=0)
{
printf("kill fail ==%d\n",val);
}
sem_wait(&s_lock);
//semaphore
//mutex
sentSignal1++;
sem_post(&s_lock);
break;
case 1:
val=pthread_kill(tid[4],SIGUSR2);
if(val!=0)
{
printf("kill fail2\n");
}
sem_wait(&s_lock);
sentSignal2++;
sem_post(&s_lock);
//
//
break;
}
i++;
total_signal_c++;
//delay for a random time, 0.01 to 0.1 second
}
}
/*
Handler 1 threads
*/
void handler1()
{
//Setting up signals
// printf("In handler1\n");
struct sigaction s;
siginfo_t info;
sigemptyset(&s.sa_mask);
//use signal to perma block for handler2
signal(SIGUSR2,handler1);
//Add Sigusr1 to set
sigaddset((&s.sa_mask),SIGUSR1);
pthread_sigmask(SIG_BLOCK,&s.sa_mask,NULL);
int val=-1;
//use signal(), sigaddset(), pthread_sigmask() etc to block and unblock signals as required.
while(1)
{ //use sigwaitinfo(); to receive a signal
val=-1;
val=sigwaitinfo(&s.sa_mask,&info);
//if signal received modify the corresponding counter
if(info.si_signo==SIGUSR1){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal1++;
//decrement semaphore lock
sem_post(&r_lock);
printf("signal 1 received\n");
}
if(val==-1)
{
// break;
}
}
pthread_exit(NULL);
}
/*
Handler2 threads
*/
void handler2()
{
int sigInfo=0;
//use signal to perma block for handler2
signal(SIGUSR1,handler2);
int val=-1;
while(1)
{ //use sigwaitinfo(); to receive a signal
val=-1;
val=sigwait(&set,&sigInfo);
//if signal received modify the corresponding counter
if(sigInfo==SIGUSR2){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal2++;
//decrement semaphore lock
sem_post(&r_lock);
printf("signal 2 received\n");
}
}
pthread_exit(NULL);
}
当存在具有相同代码的未决信号时,一些信号可能会丢失。来自 sigaction 的规范:
If a subsequent occurrence of a pending signal is generated, it is implementation-dependent as to whether the signal is delivered or accepted more than once in circumstances other than those in which queueing is required under the Realtime Signals Extension option. The order in which multiple, simultaneously pending signals outside the range SIGRTMIN to SIGRTMAX are delivered to or accepted by a process is unspecified.
如果你想捕获所有的信号,你有两个解决方案:
- 使用值从 SIGRTMIN 到 SIGRTMAX 的实时信号,而不是 SIGUSR1 和 SIGUSR2。
pthread_sigqueue()
和pthread_kill()
都将无法发送信号,如果SIGQUEUE_MAX
信号未决或者系统没有足够的资源来排队信号。 - 等待先行信号被捕捉到再发送一个。
编辑:
1。一些解释来回答你最后的评论。
您不能使用 signal()
仅阻塞信号,您可以忽略它(使用 SIG_IGN 而不是处理函数)或注册处理函数。有了处理函数,我想我们可以说信号被阻塞并被捕获。
我认为你的t.a。希望您使用 signal()
和处理函数处理一种类型的信号,例如 SIGUSR1,并使用 sigwaitinfo()
.
使用signal()
你不需要阻塞你想要捕获的信号,它可以在主线程中完成。
使用 sigwaitinfo()
你需要至少在接收它的线程中阻塞你想要捕获的信号。
你可以看看我贴在最后的源代码post。
2。更精确。
要在不放置自动 catch/handler 函数的情况下阻止信号,您必须在单线程程序中使用 sigprocmask()
,或在多线程程序中使用 pthread_sigmask()
。您还可以使用 sigaction()
以在执行信号处理函数期间阻止一些传入信号。
关于信号捕捉,有两种捕捉信号的方法:
信号处理函数在
signal()
(或sigaction()
)注册,并在接收到信号时自动调用,除非信号在所有线程中被阻塞。为了使signal()
工作,您必须至少让一个线程不阻塞信号。您不必使用sigwait()
来处理信号,因为程序会在执行的同时自动等待。使用
signal()
将在接收到信号时创建信号上下文,您将必须在信号处理函数中使用异步信号安全函数。signal()
为整个进程注册一个处理函数,而不仅仅是为调用线程。处理线程需要使用
目标的线程中使用sigwait()
或sigwaitinfo()
捕获信号,并且这些线程不限于异步信号安全函数。必须至少在作为pthread_kill()
.pthread_sigmask()
阻塞要捕获的信号并且必须在所有线程中被阻塞,以便捕获进程范围内的信号,例如由
kill()
触发的信号(如果至少一个线程没有阻塞信号,那么它将对过程)。
3。关于您的程序正在做什么的一些解释。
在主线程中,信号SIGUSR1和SIGUSR2被阻塞了,所以在这个阻塞之后主线程创建的所有线程都会阻塞这些信号,因为它们继承了创建掩码线程。
当您调用
signal()
时,它会将函数handler1()
和handler2()
注册为信号处理函数,以便在线程接收到信号时调用。但是这些信号对于所有线程都是阻塞的,所以handler1()
和handler2()
不会被调用为信号处理函数。所以,在你的程序中使用signal()
是没有用的。此外,
handler1()
和handler2()
旨在处理线程,而不是信号处理函数。所以你不应该用signal()
注册它们,你必须注册非线程函数。您应该仅在
pthread_kill()
没有失败时才增加发送信号的计数器。创建处理线程时,程序创建了2个无用线程,因为循环是针对
i = 3
和i = 4
执行的,而你在这个循环中创建了2个线程。所以正确的代码是while(i < 4)
,或者最好去掉循环。
4。我修改了你的程序,以便使用 signal()
:
捕获 SIGUSR1
你会看到它只需要在
handler2_thread()
中屏蔽SIGUSR2。程序中不需要其他阻塞。在这段代码中,您将看到处理线程和信号处理函数之间的区别,
thread1
接收到的信号由信号处理函数handler1_func()
处理,而handler2_thread
接收的信号在线程本身中处理。变量
receivedSignal1_flag
被声明为volatile
并且类型为sig_atomic_t
因为在检查和重置它的线程与将其设置为1
的处理函数。使用这种方式,一些捕捉到的信号将不会被计算在内。关于我在sig_atomic_t
上读到的内容,我不确定是否可以在handler1_func()
中直接增加计数器receivedSignal1
,因为增加操作不是原子的,所以可以被另一个信号处理程序打扰。但是如果handler_func()
是唯一一个读取和写入receivedSignal1
并声明它volatile
和sig_atomic_t
的信号处理程序,也许这是可能的。另请注意,receivedSignal1_flag
未被信号量或互斥锁锁定,因为只有一个线程在使用它。
#include<semaphore.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include<string.h>
#include<math.h>
/*
Pre-definitions of functions
*/
void generator();
void handler1_func(int);
void thread1();
void handler2_thread();
void reporter();
/*
Global Variables
*/
int total_signal_count=0;
int sentSignal1=0;
int sentSignal2=0;
///////////////////////////////////////
//
// receivedSignal1_flag is volatile and
// sig_atomic_t because there is a race
// condition on it (used in the signal
// handler, and in the thread).
//
///////////////////////////////////////
volatile sig_atomic_t receivedSignal1_flag;
int receivedSignal1=0;
int receivedSignal2=0;
sem_t s_lock;
sem_t r_lock;
pthread_mutex_t lock;
pthread_t tid[5];
/*
Main function
*/
int main(int argc, char ** argv)
{
int i=0;
int randomNum=0;
int error;
int pid;
sigset_t mask_all,mask_one,prev_one;
//Setting up signals
//Get Random time
time_t now;
time(&now);
//semaphore is initialized to be global and val 1
sem_init(&s_lock,0,1);
sem_init(&r_lock,0,1);
srand((unsigned) time(&now));
//Loops until more threads created than 2
while(i<3)
{ error=pthread_create(&tid[i],NULL,(void*)generator,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
i++;
}//end while loop
error=pthread_create(&tid[3],NULL,(void*)thread1,NULL);
if(error!=0)
{
printf("failed to create thread\n");
}
error=pthread_create(&tid[4],NULL,(void*)handler2_thread,NULL);
if(error!=0)
{
printf("failed to create thread \n");
}
//join the threads so main won't return
i=0;
int returnVal;
sleep(15);
printf("\n sigSent1==%d,sigSent2==%d,sigReceived1==%d,sigReceived2==%d\n",sentSignal1,sentSignal2,receivedSignal1,receivedSignal2);
while(i<5)//Loops until threads are joined
{
// printf("gonna join %d\n",i);
pthread_join(tid[i],NULL);
/*if((returnVal=pthread_join(tid[i],(void**)&returnVal))!=0)
{
printf("Error joining thread: %s at %d\n", strerror(returnVal),i);
}*/
i++;
}//end while
return 0;
}//end of main function
/*
Generator threads
*/
void generator()
{
sleep(5);
int i=3;
int randomNum=0;
int val=0;
int total_signal_c=9990;
while(total_signal_c<10000)
{
usleep(1);
//Randomly select to generate SIGUSR1 or SIGUSR2
//Use pthread_kill(tid,SIGUSR1/SIGUSR2) to send the signal to a thread
// printf("total_signal_count%d\n",total_signal_c);
//Create either a sig1 signal or sig2 signal
randomNum=rand()%2;
switch(randomNum)
{
case 0:
/////////////////////////////////////////
// Send SIGUSR1 to thread1
/////////////////////////////////////////
val=pthread_kill(tid[3],SIGUSR1);
if(val!=0)
{
printf("\nkill fail ==%d",val);
} else {
sem_wait(&s_lock);
//semaphore
//mutex
sentSignal1++;
sem_post(&s_lock);
}
break;
case 1:
/////////////////////////////////////////
// Send SIGUSR2 to handler2_thread
/////////////////////////////////////////
val=pthread_kill(tid[4],SIGUSR2);
if(val!=0)
{
printf("\nkill fail2");
} else {
sem_wait(&s_lock);
sentSignal2++;
sem_post(&s_lock);
//
//
}
break;
}
i++;
total_signal_c++;
//delay for a random time, 0.01 to 0.1 second
}
}
//////////////////////////////////////////
//
// Signal handler function for SIGUSR1:
//
//////////////////////////////////////////
void handler1_func(int signo)
{
// write on stdout using an async-signal-safe function:
write(STDOUT_FILENO,"\nSignal handler function: SIGUSR1 caught\n",41);
// set the received signal flag to 1:
if(signo == SIGUSR1) receivedSignal1_flag = 1;
}
/////////////////////////////////////////////////////////////
//
// The thread that will receive SIGUSR1 but not handle it
// because handler1_func() will handle it automatically:
//
/////////////////////////////////////////////////////////////
void thread1()
{
//////////////////////////////////////////////
//
// register handler1_func() as signal handler
// for the whole process, not only the thread.
// It means that if another thread doesn't
// block SIGUSR1 and receive it, then
// handler1_func() will also be called:
//
//////////////////////////////////////////////
signal(SIGUSR1,handler1_func);
while(1)
{
///////////////////////////////////////////////////
// If a signal has been handled by handler1_func()
// then receivedSignal1_flag = 1.
// And so increment receivedSignal1 and print.
///////////////////////////////////////////////////
if(receivedSignal1_flag == 1) {
// reset the flag:
receivedSignal1_flag = 0;
sem_wait(&r_lock);
receivedSignal1++;
printf("\nThread1: SIGUSR1 received and handled by handler1_func()\n");
sem_post(&r_lock);
}
}
pthread_exit(NULL);
}
////////////////////////////////////////
//
// Handling thread for SIGUSR2:
//
////////////////////////////////////////
void handler2_thread()
{
///////////////////////////////////////////////
//
// Need to block SIGUSR2 in order to avoid
// the default handler to be called.
//
///////////////////////////////////////////////
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGUSR2);
pthread_sigmask(SIG_BLOCK,&set,NULL);
siginfo_t info;
int val=-1;
while(1)
{
val=-1;
val=sigwaitinfo(&set,&info);
//if signal received modify the corresponding counter
if(info.si_signo==SIGUSR2){
//increment semaphore lock
sem_wait(&r_lock);
receivedSignal2++;
//decrement semaphore lock
printf("\nhandler2_thread: signal 2 received\n");
sem_post(&r_lock);
}
}
pthread_exit(NULL);
}
只能从信号处理程序中安全地调用异步信号安全函数。
sigwait()
和sigwaitinfo()
不是 异步信号安全的。请参阅 2.4 信号概念 ,位于 http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html. Also see the Linuxsignal.7
man page。printf()
异步信号也不安全。在信号处理程序中调用
pthread_exit()
是未定义的行为。它将终止线程 - 但在信号处理上下文中,可能会导致重大问题。以下问题刚刚开始涉及在信号处理程序中调用pthread_exit()
导致的问题:pthread_exit() in signal handler and How to properly terminate a thread in a signal handler? See also http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html
从根本上说,您的代码很混乱。您将 handler1()
和 handler2()
作为单独的线程启动,然后将这些相同的函数注册为信号处理程序,然后在函数中调用 sigwait()
/sigwaitinfo()
。
鉴于代码结合线程、信号处理程序、while(1)...
循环的方式,几乎不可能开始猜测发生了什么。例如,您可能会得到生成卡在无限循环中的信号处理程序的线程。
这行代码:
signal(SIGUSR1,handler2);
表示当收到 SIGUSR1
时,将在信号上下文中调用 handler2()
- 但是 handler2()
中有一个 while(1)
循环...
异步信号处理是一个很难掌握的概念。我会说你需要从一些比多个线程试图相互发送信号更简单的事情开始。