多线程中的信号处理

多线程中的信号处理

在linux下写服务器,处理信号在所难免。在多线程和单线程中信号的处理还是有点不同的。
参考:多线程中的信号处理.

问题

前言:
在linux下,每个进程都有自己的signal mask,这个信号掩码指定哪个信号被阻塞,哪个不会被阻塞,通常用调用sigmask来处理。同时一个进程还有自己的signal action,这个行为集合指定了信号在进程内的所有线程该如何处理,通常调用sigaction来处理。

使用了多线程后,便有些疑问:

  1. 信号发生时,哪个线程会收到
  2. 是不是每个线程都有自己的mask及action
  3. 每个线程能按自己的方式处理信号么

  1. 如果是异常产生的信号(比如程序错误,像SIGPIPE、SIGEGV这些),则只有产生异常的线程收到并处理。

  2. 如果是用pthread_kill产生的内部信号,则只有pthread_kill参数中指定的目标线程收到并处理。

  3. 如果是外部使用kill命令产生的信号,通常是SIGINT、SIGHUP等job control信号,则会遍历所有线程,直到找到一个不阻塞该信号的线程,然后调用它来处理。(一般从主线程找起),注意只有一个线程能收到。

  4. 其次,每个线程都有自己独立的signal mask,但所有线程共享进程的signal action。这意味着,你可以在线程中调用pthread_sigmask(不是sigmask)来决定本线程阻塞哪些信号。但你不能调用sigaction来指定单个线程的信号处理方式。如果在某个线程中调用了sigaction处理某个信号,那么这个进程中的未阻塞这个信号的所有线程在收到这个信号都会按同一种方式处理这个信号。另外,注意子线程的mask是会从主线程继承而来的。

  5. 第三个问题,因为signal action共享的问题,已经知道不能。

注:本文把sigaction设置信号处理方式简称为”sigaction注册“

1.sigaction注册时机

sigaction在主线程中注册

#include <pthread.h>
#include <stdio.h>
#include <sys/signal.h>
#define NUMTHREADS 3
void sighand(int signo);

void *threadfunc(void *parm) {
	pthread_t             tid = pthread_self();
	int                   rc;
	printf("No blocking thread=%u\n", tid%1000);
	rc = sleep(30); /* 若有信号中断则返回已经运行的秒数 */
	if (rc>0) {
		printf("No blocking pthread = %u ,Sleep was interrupted for %d seconds(非阻塞线程的sleep被信号打断)\n",  tid%1000, 30-rc);
	} else {
		printf("pthread = %u , success running!(非阻塞线程成功运行)\n",  tid%1000);
	}
	return NULL;
}

void *threadmasked(void *parm) {
	pthread_t             tid = pthread_self();
	sigset_t              mask;
	int                   rc;

	printf("Yes blocking thread=%u\n", tid%1000);

	sigfillset(&mask); /* 将所有信号加入mask信号集 */

	/* 向当前的信号掩码中添加mask信号集 */
	rc = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (rc != 0) {
		printf("%d, %s\n", rc, strerror(rc));
		return NULL;
	}

	rc = sleep(10);
	if (rc>0) {
		printf("Yes blocking pthread = %u ,Sleep was interrupted for %d seconds(阻塞线程的sleep被信号打断)\n",  tid%1000, 10-rc);
	} else {
		printf("pthread = %u , success running!(阻塞线程正常运行结束)\n",  tid%1000);
	}
	return NULL;
}

int main() {
	int                     rc;
	int                     i;
	pthread_t               threads[NUMTHREADS];
	pthread_t               maskedthreads[NUMTHREADS];

	printf("Main pthread start\n");
	/// 
	printf("Main中开始注册SIGALRM CallBack\n");
	struct sigaction        actions;
	memset(&actions, 0, sizeof(actions));
	sigemptyset(&actions.sa_mask); /* 将参数set信号集初始化并清空 */
	actions.sa_flags = 0;
	actions.sa_handler = sighand;
	rc = sigaction(SIGALRM,&actions,NULL);/*设置SIGALRM的处理函数*/
	printf("Main中成功注册SIGALRM CallBack\n");
	/// 

	printf("===>Create masked and unmasked threads Start<===\n");
	for(i=0; i<NUMTHREADS; ++i) {
		rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
		if (rc != 0) {
			printf("%d, %s/n", rc, strerror(rc));
			return -1;
		}

		rc = pthread_create(&maskedthreads[i], NULL, threadmasked, NULL);
		if (rc != 0) {
			printf("%d, %s\n", rc, strerror(rc));
			return -1;
		}
	}
	printf("===>Create masked and unmasked threads Stop<===\n");
	sleep(3);
	printf("===>Send a signal to masked and unmasked threads<===\n");

	/* 向线程发送SIGALRM信号 */
	for(i=0; i<NUMTHREADS; ++i) {
		rc = pthread_kill(threads[i], SIGALRM);
		rc = pthread_kill(maskedthreads[i], SIGALRM);
	}


	for(i=0; i<NUMTHREADS; ++i) {
		rc = pthread_join(threads[i], NULL);
		rc = pthread_join(maskedthreads[i], NULL);
	}

	printf("Main Stop\n");
	return 0;
}

void sighand(int signo) {
	pthread_t             tid = pthread_self();
	printf("<<<Thread [%lu] in signal CallBack(信号回调函数)>>>\n", tid%1000);
	return;
}
Main中开始注册SIGALRM CallBack
Main中成功注册SIGALRM CallBack
No blocking thread=656
Yes blocking thread=952
No blocking thread=248
Yes blocking thread=544
No blocking thread=840
Yes blocking thread=136
<<<Thread [656] in signal CallBack(信号回调函数)>>>

<<<Thread [248] in signal CallBack(信号回调函数)>>>

<<<Thread [840] in signal CallBack(信号回调函数)>>>

sigaction在子线程中注册

#include <signal.h>
#include <pthread.h>
#include <stdio.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
	pthread_t   tid = pthread_self();
	int     rc;

	printf("thread 1 with tid:%u\n", tid%1000);
	rc = sleep(60);
	if (rc > 0)
		printf("thread 1 被信号中断\n");
		
	printf("thread 1 ends\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	struct sigaction    actions;
	pthread_t       tid = pthread_self();
	int         rc, err;
	printf("thread 2 with tid:%u\n", tid%1000);
	/// 
	printf("thread2中开始注册SIGALRM CallBack\n");
	memset(&actions, 0, sizeof(actions));
	sigemptyset(&actions.sa_mask); /* 将参数set信号集初始化并清空 */
	actions.sa_flags = 0;
	actions.sa_handler = sighandler;
	rc = sigaction(SIGALRM,&actions,NULL);/*设置SIGALRM的处理函数*/
	printf("thread2中成功注册SIGALRM CallBack\n");
	/// 
	rc = sleep(60);
	if (rc > 0)
		printf("thread 2 被信号中断\n");
	printf("thread 2 ends\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t   tid = pthread_self();
	sigset_t    mask;
	int     rc, err;

	printf("thread 3 with tid:%u\n", tid%1000);

	///
	printf("thread 3 开始设置阻塞SIGALRM信号\n"); 
	sigemptyset(&mask); /* 初始化mask信号集 */
	sigaddset(&mask, SIGALRM);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("%d, %s/n", rc, strerror(rc));
		return NULL;
	}
	printf("thread 3 成功设置阻塞SIGALRM信号\n");  
	///
	rc = sleep(10);
	if (rc != 0)
		printf("thread 3 被信号中断\n");
	printf("thread 3 正常结束运行ends\n");
	return NULL;
}

int main(void) {
	int     rc, err;
	pthread_t   thr1, thr2, thr3, thrm = pthread_self();

	printf("thread main with pid %u\n", (unsigned int)thrm);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
		exit(1);
	}
	/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2 set the signal handler, then the whole process will be terminated*/
	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
		exit(1);
	}
	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n",err, strerror(rc));
		exit(1);
	}
	sleep(3);/*wait for the threads to register.*/
	pthread_kill(thr1, SIGALRM);
	pthread_kill(thr2, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_join(thr1, NULL);   
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	printf("main ends\n");
	return 0;
}

void sighandler(int signo) {
	pthread_t   tid = pthread_self();
	printf("<<<thread with pid:%u receive signo:%d>>>\n", tid%1000, signo);
	return;
}
thread main with pid 2563262272
thread 1 with tid:96
thread 2 with tid:392
thread2中开始注册SIGALRM CallBack

<<<thread with pid:96 receive signo:14>>>

<<<thread with pid:392 receive signo:14>>>

sigaction注册不同信号

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGINT, &action, NULL);//注册crtl+c 信号 
	printf("注册SIGINT CallBack 成功\n");
	/// 
	pthread_t tid = pthread_self();
	int rc;

	printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid%1000);
	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 1 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功\n");
	/// 
	
	pthread_t tid = pthread_self();
	int rc, err;

	printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid%1000);

	

	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 2 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t tid = pthread_self();
	sigset_t mask;
	int rc, err;

	printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid%1000);

	sigemptyset(&mask); /* 初始化mask信号集 */
	sigaddset(&mask, SIGALRM);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("%d, %s/n", rc, strerror(rc));
		return NULL;
	}
	printf("thread3 开始阻塞SIGALRM\n");
	rc = sleep(10);
	if (rc != 0)
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		
	printf("thread3 解除阻塞SIGALRM\n");
	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽 
	if (err != 0) {
		printf("unblock %d, %s\n", rc, strerror(rc));
		return NULL;
	}
	printf("thread 3 sleep 10s start test 现在发送SIGALRM即可接收\n");
	rc = sleep(10);
	if (rc > 0){
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		printf("err~err~err~err~err----thread 3 sleep 10s test interrupted----err~err~err~err~err\n");
	}
	else {
		printf("thread 3 sleep 10s end test\n");
	}
	printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
	return NULL;
}

int main(void) {
	int rc, err;
	pthread_t thr1, thr2, thr3, thrm = pthread_self();

	printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm%1000);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2
	 * set the signal handler, then the whole process will be terminated*/
	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	sleep(10);
	//内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
	printf("=======>Send 5 SIGALRM<=======\n");
	pthread_kill(thr1, SIGALRM);
	pthread_kill(thr2, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	sleep(15);//main执行10~20之间打断 
	printf("=======>Send 1 SIGALRM for pthread 3<=======\n");
	pthread_kill(thr3, SIGALRM);
	pthread_join(thr1, NULL); /*wait for the threads to complete.*/
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	sleep(99999);//供 crtl+c 给主线程结束 
	printf("main ends\n");
	return 0;
}

void sighandler(int signo) {
	pthread_t tid = pthread_self();

	printf("\nCallBcak thread pid:%lu receive signo:%d\n", tid%1000, signo);
	return;
}
~~~~~~~~~~~thread main pid 768~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:96~~~~~~~~~~~
注册SIGALRM CallBack 成功
~~~~~~~~~~~thread 2 tid:392~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:688~~~~~~~~~~~
thread3 开始阻塞SIGALRM
^C
CallBcak thread pid:768 receive signo:2 //主线程触发SIGINT回调


CallBcak thread pid:392 receive signo:14


CallBcak thread pid:96 receive signo:14



CallBcak thread pid:688 receive signo:14//123-125行给thread3发送了3条SIGALRM,结果都被阻塞了,所以解

CallBcak thread pid:768 receive signo:2//主线程触发SIGINT回调

CallBcak thread pid:688 receive signo:14

^C
CallBcak thread pid:768 receive signo:2//主线程触发SIGINT回调

sigaction注册相同信号

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler1(int signo);
void sighandler2(int signo);
void sighandler3(int signo);

void *thr1_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler1;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功 from thread 1\n");
	/// 
	pthread_t tid = pthread_self();
	int rc;

	printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid%1000);
	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 1 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	sleep(1);
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler2;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功 from thread 2\n");
	/// 
	
	pthread_t tid = pthread_self();
	int rc, err;

	printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid%1000);

	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 2 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t tid = pthread_self();
	sigset_t mask;
	int rc, err;

	printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid%1000);

	rc = sleep(10);
	if (rc != 0)
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);

	printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
	return NULL;
}

int main(void) {
	int rc, err;
	pthread_t thr1, thr2, thr3, thrm = pthread_self();

	//
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler3;
	sigaction(SIGINT, &action, NULL);//注册crtl+c 信号 
	printf("注册SIGINT CallBack 成功\n");
	//

	printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm%1000);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	printf("=======>Start sending signal<=======\n");
	pthread_kill(thr1, SIGALRM);
	sleep(1);
	pthread_kill(thr2, SIGALRM);
	sleep(1);
	pthread_kill(thr3, SIGALRM);
	
	
	pthread_join(thr1, NULL); /*wait for the threads to complete.*/
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	sleep(99999);//供 crtl+c 给主线程结束 
	printf("main ends\n");
	return 0;
}

void sighandler1(int signo) {
	pthread_t tid = pthread_self();
	printf("\nCallBcak thread pid:%lu receive signo:%d callback is 1\n", tid%1000, signo);
	return;
}
void sighandler2(int signo) {
	pthread_t tid = pthread_self();
	printf("\nCallBcak thread pid:%lu receive signo:%d callback is 2\n", tid%1000, signo);
	return;
}
void sighandler3(int signo) {
	pthread_t tid = pthread_self();
	printf("\nCallBcak thread pid:%lu receive signo:%d callback is 3\n", tid%1000, signo);
	return;
}

~~~~~~~~~~~thread main pid 544~~~~~~~~~~~
注册SIGALRM CallBack 成功 from thread 1
~~~~~~~~~~~thread 1 tid:872~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:464~~~~~~~~~~~

CallBcak thread pid:872 receive signo:14 callback is 1
注册SIGALRM CallBack 成功 from thread 2
~~~~~~~~~~~thread 2 tid:168~~~~~~~~~~~

CallBcak thread pid:168 receive signo:14 callback is 2

CallBcak thread pid:464 receive signo:14 callback is 2
^C
CallBcak thread pid:544 receive signo:2 callback is  3

2.哪个线程接收信号并处理?

主线程不阻塞SIGINT信号

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
	
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGINT, &action, NULL);//注册crtl+c 信号 
	printf("注册SIGINT CallBack 成功\n");
	/// 
	pthread_t tid = pthread_self();
	int rc;

	printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid%1000);
	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 1 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功\n");
	/// 
	
	pthread_t tid = pthread_self();
	int rc, err;

	printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid%1000);

	

	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 2 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t tid = pthread_self();
	sigset_t mask;
	int rc, err;

	printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid%1000);

	sigemptyset(&mask); /* 初始化mask信号集 */
	sigaddset(&mask, SIGALRM);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("%d, %s/n", rc, strerror(rc));
		return NULL;
	}
	printf("thread3 开始阻塞SIGALRM\n");
	rc = sleep(10);
	if (rc != 0)
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		
	printf("thread3 解除阻塞SIGALRM\n");
	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽 
	if (err != 0) {
		printf("unblock %d, %s\n", rc, strerror(rc));
		return NULL;
	}
	printf("thread 3 sleep 10s start test 现在发送SIGALRM即可接收\n");
	rc = sleep(10);
	if (rc > 0){
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		printf("err~err~err~err~err----thread 3 sleep 10s test interrupted----err~err~err~err~err\n");
	}
	else {
		printf("thread 3 sleep 10s end test\n");
	}
	printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
	return NULL;
}

int main(void) {
	int rc, err;
	pthread_t thr1, thr2, thr3, thrm = pthread_self();

	printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm%1000);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2
	 * set the signal handler, then the whole process will be terminated*/
	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	sleep(10);
	//内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
	printf("=======>Send 5 SIGALRM<=======\n");
	pthread_kill(thr1, SIGALRM);
	pthread_kill(thr2, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	sleep(15);//main执行10~20之间打断 
	printf("=======>Send 1 SIGALRM for pthread 3<=======\n");
	pthread_kill(thr3, SIGALRM);
	pthread_join(thr1, NULL); /*wait for the threads to complete.*/
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	sleep(99999);//供 crtl+c 给主线程结束 
	printf("main ends\n");
	return 0;
}

void sighandler(int signo) {
	pthread_t tid = pthread_self();

	printf("\nCallBcak thread pid:%lu receive signo:%d\n", tid%1000, signo);
	return;
}
~~~~~~~~~~~thread main pid 768~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:96~~~~~~~~~~~
注册SIGALRM CallBack 成功
~~~~~~~~~~~thread 2 tid:392~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:688~~~~~~~~~~~
^C
CallBcak thread pid:768 receive signo:2 //主线程触发SIGINT回调

^C
CallBcak thread pid:768 receive signo:2//主线程触发SIGINT回调

^C
CallBcak thread pid:768 receive signo:2//主线程触发SIGINT回调

主线程创建子线程之前阻塞SIGINT信号

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
	
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGINT, &action, NULL);//注册crtl+c 信号 
	printf("注册SIGINT CallBack 成功\n");
	/// 

	
	
//	sigset_t mask;
//	sigemptyset(&mask); /* 初始化mask信号集 */
//	sigaddset(&mask, SIGINT);
//	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽
	
//上面代码取消注释,设置自己线程的阻塞信号集,即可接收到信号,因为信号优先发给主线程,如果主线程不能处理,则找别的线程
	pthread_t tid = pthread_self();
	int rc;

	printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid%1000);
	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 1 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功\n");
	/// 
	pthread_t tid = pthread_self();
	int rc, err;

	printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid%1000);

	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 2 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t tid = pthread_self();
	sigset_t mask;
	int rc, err;

	printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid%1000);

	sigemptyset(&mask); /* 初始化mask信号集 */

	sigaddset(&mask, SIGALRM);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("%d, %s/n", rc, strerror(rc));
		return NULL;
	}
	rc = sleep(10);
	if (rc != 0)
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
	printf("pthread 3 SIG_UNBLOCK now can recv SIGALARM\n");
	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽
	if (err != 0) {
		printf("unblock %d, %s\n", rc, strerror(rc));
		return NULL;
	}
	printf("thread 3 sleep 10s start test\n");
	rc = sleep(10);
	if (rc > 0) {
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		printf("err~err~err~err~err----thread 3 sleep 10s test interrupted----err~err~err~err~err\n");
	} else {
		printf("thread 3 sleep 10s end test\n");
	}
	printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
	return NULL;
}

int main(void) {
	int rc, err;
	pthread_t thr1, thr2, thr3, thrm = pthread_self();
	/// 
	sigset_t mask;
	sigemptyset(&mask); /* 初始化mask信号集 */
	sigaddset(&mask, SIGINT);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("main pthread_sigmask %s/n", rc, strerror(rc));
		return NULL;
	}
	/// 

	printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm%1000);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2
	 * set the signal handler, then the whole process will be terminated*/
	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}
	
	sleep(10);
	//内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
	printf("=======>Send 5 SIGALRM<=======\n");
	pthread_kill(thr1, SIGALRM);
	pthread_kill(thr2, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	sleep(15);//main执行10~20之间打断
	printf("=======>Send 1 SIGALRM for pthread 3<=======\n");
	pthread_kill(thr3, SIGALRM);
	pthread_join(thr1, NULL); /*wait for the threads to complete.*/
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	sleep(20);//供 crtl+c 给主线程结束
	printf("main ends\n");
	return 0;
}

void sighandler(int signo) {
	pthread_t tid = pthread_self();

	printf("\nCallBcak thread pid:%lu receive signo:%d\n", tid%1000, signo);
	return;
}
~~~~~~~~~~~thread main pid 152~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:480~~~~~~~~~~~
注册SIGALRM CallBack 成功
~~~~~~~~~~~thread 2 tid:776~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:72~~~~~~~~~~~
^C
^C
^C
^C
^C
^C
^C
^C
^C
^C
^C
^C
由于线程123都没有设置自己的阻塞信号集,所以默认使用主线程的阻塞信号集,此时所有线程都阻塞了SIGINT

主线程创建子线程之后阻塞SIGINT信号

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
	
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGINT, &action, NULL);//注册crtl+c 信号 
	printf("注册SIGINT CallBack 成功\n");
	/// 

	
	
//	sigset_t mask;
//	sigemptyset(&mask); /* 初始化mask信号集 */
//	sigaddset(&mask, SIGINT);
//	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽
	

	pthread_t tid = pthread_self();
	int rc;

	printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid%1000);
	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 1 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr2_fn(void *arg) {
	/// 
	struct sigaction action;
	memset(&action, 0, sizeof(action));
	action.sa_flags = 0;
	action.sa_handler = sighandler;
	sigaction(SIGALRM, &action, NULL);
	printf("注册SIGALRM CallBack 成功\n");
	/// 
	pthread_t tid = pthread_self();
	int rc, err;

	printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid%1000);

	rc = sleep(60);
	if (rc != 0)
		printf("===>thread 2 run %d s ,should 60s because interrupted<===\n", 60 - rc);
	printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
	return NULL;
}

void *thr3_fn(void *arg) {
	pthread_t tid = pthread_self();
	sigset_t mask;
	int rc, err;

	printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid%1000);

	sigemptyset(&mask); /* 初始化mask信号集 */

	sigaddset(&mask, SIGALRM);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("%d, %s/n", rc, strerror(rc));
		return NULL;
	}
	rc = sleep(10);
	if (rc != 0)
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
	printf("pthread 3 SIG_UNBLOCK now can recv SIGALARM\n");
	err = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);//解除SIGALARM的屏蔽
	if (err != 0) {
		printf("unblock %d, %s\n", rc, strerror(rc));
		return NULL;
	}
	printf("thread 3 sleep 10s start test\n");
	rc = sleep(10);
	if (rc > 0) {
		printf("===>thread 3 run %d s ,should 10s because interrupted<===\n", 10 - rc);
		printf("err~err~err~err~err----thread 3 sleep 10s test interrupted----err~err~err~err~err\n");
	} else {
		printf("thread 3 sleep 10s end test\n");
	}
	printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
	return NULL;
}

int main(void) {
	int rc, err;
	pthread_t thr1, thr2, thr3, thrm = pthread_self();

	printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm%1000);
	err = pthread_create(&thr1, NULL, thr1_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	/*  pthread_kill(thr1, SIGALRM);    send a SIGARLM signal to thr1 before thr2
	 * set the signal handler, then the whole process will be terminated*/
	err = pthread_create(&thr2, NULL, thr2_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}

	err = pthread_create(&thr3, NULL, thr3_fn, NULL);
	if (err != 0) {
		printf("error in creating pthread:%d\t%s\n", err, strerror(rc));
		exit(1);
	}
	/// 
	sigset_t mask;
	sigemptyset(&mask); /* 初始化mask信号集 */
	sigaddset(&mask, SIGINT);
	err = pthread_sigmask(SIG_BLOCK, &mask, NULL);
	if (err != 0) {
		printf("main pthread_sigmask %s/n", rc, strerror(rc));
		return NULL;
	}
	/// 
	sleep(10);
	//内部产生的信号,只有指定的线程能收到,因此要向所有线程发送
	printf("=======>Send 5 SIGALRM<=======\n");
	pthread_kill(thr1, SIGALRM);
	pthread_kill(thr2, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	pthread_kill(thr3, SIGALRM);
	sleep(15);//main执行10~20之间打断
	printf("=======>Send 1 SIGALRM for pthread 3<=======\n");
	pthread_kill(thr3, SIGALRM);
	pthread_join(thr1, NULL); /*wait for the threads to complete.*/
	pthread_join(thr2, NULL);
	pthread_join(thr3, NULL);
	sleep(20);//供 crtl+c 给主线程结束
	printf("main ends\n");
	return 0;
}

void sighandler(int signo) {
	pthread_t tid = pthread_self();

	printf("\nCallBcak thread pid:%lu receive signo:%d\n", tid%1000, signo);
	return;
}
~~~~~~~~~~~thread main pid 0~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:328~~~~~~~~~~~
注册SIGALRM CallBack 成功
~~~~~~~~~~~thread 2 tid:624~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:920~~~~~~~~~~~
^C
CallBcak thread pid:328 receive signo:2//线程1触发的SIGINT回调
~~~~~~~~~~~thread 1 ends~~~~~~~~~~~
^C
CallBcak thread pid:624 receive signo:2//线程2触发的SIGINT回调
~~~~~~~~~~~thread 2 ends~~~~~~~~~~~
^C
CallBcak thread pid:920 receive signo:2//线程3触发的SIGINT回调
~~~~~~~~~~~thread 3 ends~~~~~~~~~~~
^C//由于主线程阻塞了SIGINT,所以始终结束不了
^C
main ends

主线程阻塞SIGINT信号,都不阻塞的子线程哪个处理该信号?

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sighandler(int signo);

void *thr1_fn(void *arg) {
  
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  action.sa_flags = 0;
  action.sa_handler = sighandler;
  sigaction(SIGINT, &action, NULL); //注册crtl+c 信号
  printf("注册SIGINT CallBack 成功\n");
  ///

  pthread_t tid = pthread_self();
  int rc;

  printf("~~~~~~~~~~~thread 1 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  rc = sleep(60);
  if (rc != 0)
    printf("===>thread 1 run %d s ,should 60s because interrupted<===\n",
           60 - rc);
  printf("~~~~~~~~~~~thread 1 ends~~~~~~~~~~~\n");
  return NULL;
}

void *thr2_fn(void *arg) {
  pthread_t tid = pthread_self();
  int rc, err;
  printf("~~~~~~~~~~~thread 2 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  rc = sleep(60);
  if (rc != 0)
    printf("===>thread 2 run %d s ,should 60s because interrupted<===\n",
           60 - rc);
  printf("~~~~~~~~~~~thread 2 ends~~~~~~~~~~~\n");
  return NULL;
}

void *thr3_fn(void *arg) {
  pthread_t tid = pthread_self();
  printf("~~~~~~~~~~~thread 3 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  int rc;
  rc = sleep(60);
  if (rc > 0) {
    printf("===>thread 3 run %d s ,should 10s because interrupted<===\n",
           10 - rc);
    printf("~~~~~~~~~~~thread 3 ends~~~~~~~~~~~\n");
    return NULL;
  }
}
void *thr4_fn(void *arg) {
  pthread_t tid = pthread_self();
  int rc, err;
  printf("~~~~~~~~~~~thread 4 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  rc = sleep(60);
  if (rc != 0)
    printf("===>thread 4 run %d s ,should 60s because interrupted<===\n",
           60 - rc);
  printf("~~~~~~~~~~~thread 4 ends~~~~~~~~~~~\n");
  return NULL;
}
void *thr5_fn(void *arg) {
  pthread_t tid = pthread_self();
  int rc, err;
  printf("~~~~~~~~~~~thread 5 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  rc = sleep(60);
  if (rc != 0)
    printf("===>thread 5 run %d s ,should 60s because interrupted<===\n",
           60 - rc);
  printf("~~~~~~~~~~~thread 5 ends~~~~~~~~~~~\n");
  return NULL;
}
void *thr6_fn(void *arg) {
  pthread_t tid = pthread_self();
  int rc, err;
  printf("~~~~~~~~~~~thread 6 tid:%lu~~~~~~~~~~~\n", tid % 1000);
  rc = sleep(60);
  if (rc != 0)
    printf("===>thread 6 run %d s ,should 60s because interrupted<===\n",
           60 - rc);
  printf("~~~~~~~~~~~thread 6 ends~~~~~~~~~~~\n");
  return NULL;
}

int main(void) {
  int rc, err;
  pthread_t thr1, thr2, thr3, thr4, thr5, thr6, thrm = pthread_self();

  printf("~~~~~~~~~~~thread main pid %lu~~~~~~~~~~~\n", thrm % 1000);
  err = pthread_create(&thr1, NULL, thr1_fn, NULL);

  err = pthread_create(&thr2, NULL, thr2_fn, NULL);

  err = pthread_create(&thr3, NULL, thr3_fn, NULL);

  err = pthread_create(&thr4, NULL, thr4_fn, NULL);

  err = pthread_create(&thr5, NULL, thr5_fn, NULL);

  err = pthread_create(&thr6, NULL, thr6_fn, NULL);

  /
  sigset_t mask;
  sigemptyset(&mask); /* 初始化mask信号集 */
  sigaddset(&mask, SIGINT);
  err = pthread_sigmask(SIG_BLOCK, &mask, NULL); //主线程阻塞SIGINT
  if (err != 0) {
    printf("main pthread_sigmask %s/n", rc, strerror(rc));
    return NULL;
  }
  /
  for (int i = 0; i < 6; i++) {
    sleep(2); //延迟的原因是,可能出现第一次信号给了线程1,然后后面的信号都给线程1,
    //在线程1回到函数处理过程中的时候,该信号是被屏蔽的
    kill(getpid(), SIGINT); //给进程发送三次SIGINT信号,观察哪些线程处理了该信号
  }

  sleep(30);
  printf("main ends\n");
  return 0;
}

void sighandler(int signo) {
  pthread_t tid = pthread_self();
  printf("\nCallBcak thread pid:%lu receive signo:%d\n", tid, signo);
  return;
}

~~~~~~~~~~~thread main pid 192~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:520~~~~~~~~~~~
~~~~~~~~~~~thread 2 tid:816~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:112~~~~~~~~~~~
~~~~~~~~~~~thread 4 tid:408~~~~~~~~~~~
~~~~~~~~~~~thread 5 tid:704~~~~~~~~~~~
~~~~~~~~~~~thread 6 tid:0~~~~~~~~~~~

CallBcak thread pid:140258755147520 receive signo:2


CallBcak thread pid:140258746754816 receive signo:2


CallBcak thread pid:140258738362112 receive signo:2


CallBcak thread pid:140258729969408 receive signo:2


CallBcak thread pid:140258721576704 receive signo:2


CallBcak thread pid:140258713184000 receive signo:2


~~~~~~~~~~~thread main pid 712~~~~~~~~~~~
注册SIGINT CallBack 成功
~~~~~~~~~~~thread 1 tid:40~~~~~~~~~~~
~~~~~~~~~~~thread 2 tid:336~~~~~~~~~~~
~~~~~~~~~~~thread 3 tid:632~~~~~~~~~~~
~~~~~~~~~~~thread 5 tid:224~~~~~~~~~~~
~~~~~~~~~~~thread 4 tid:928~~~~~~~~~~~
~~~~~~~~~~~thread 6 tid:520~~~~~~~~~~~

CallBcak thread pid:139913285687040 receive signo:2


CallBcak thread pid:139913277294336 receive signo:2


CallBcak thread pid:139913268901632 receive signo:2


CallBcak thread pid:139913260508928 receive signo:2


CallBcak thread pid:139913252116224 receive signo:2


CallBcak thread pid:139913243723520 receive signo:2

结论

1.sigaction注册时机

  1. sigaction不论在主线程中注册还是在子线程中注册,最终进程内的所有线程都调用sigaction注册的回调函数来处理。
  2. sigaction注册不同信号,则不同信号对应着sigaction注册时的回调函数
  3. sigaction注册相同信号,相同信号被注册多个回调函数,此时信号发来,执行的是最新的一个回调函数,等又有一个sigaction注册了之前的信号,则之前信号的回调函数会被更新会新的回调函数

2.哪个线程处理信号

  1. 如果主线程不阻塞某个信号,则优先处理信号的线程是主线程,且一个信号只会被一个线程所处理
  2. 当主线程阻塞了某个信号,则交由子线程处理,通过多次测试得出,优先处理信号的线程是pthread_ID较大的那个
  3. 虽然每个线程都有自己独立的signal mask,但是要注意子线程的mask是会从主线程继承而来的,如果主线程先阻塞了某个信号,再创建子线程,而子线程没有设置自己的signal mask ,则子线程也默认阻塞了该信号
posted @ 2021-09-21 22:15  cheems~  阅读(206)  评论(0编辑  收藏  举报