本文会对信号的一些需要注意的地方进行详细的总结,下面所介绍的函数的定义可以在上一篇文章中查看linux信号在多线程中的使用1。
sigaction
在多线程环境中,主线程中创建的sigaction
结构体会被每个子线程所继承。当你在主线程中调用sigaction
函数来设置信号处理函数时,这些设置会应用于整个进程,包括主线程和所有未来创建的子线程。这是因为线程继承了父线程的信号处理设置。一旦新线程创建成功,它会从创建它的线程(父线程)那里继承信号处理设置,包括sigaction
注册的信号处理函数。这意味着,如果你在主线程中使用sigaction
注册了某个信号的处理函数,那么该信号的处理函数会被继承到所有由主线程创建的子线程中。
示例1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
void getCurrentTime(char* curtime)
{
time_t currentTime;
struct tm* timeInfo;
char timeString[80];
// 获取当前时间
currentTime = time(NULL);
// 将时间转换为本地时间
timeInfo = localtime(¤tTime);
// 将时间格式化为字符串
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo);
strcpy(curtime,timeString);
}
void sig_handler(int signum) {
char curtime[80];
getCurrentTime(curtime);
printf("Signal %d received in thread %lu,time:%s\n", signum, pthread_self(),curtime);
}
void* thread_function(void* arg) {
// 线程函数中的代码...
// 子线程继承了主线程的信号处理设置,包括信号处理函数。
// 子线程可以进行其他操作...
int t = sleep(5);
printf("t:%d\n",t);
return NULL;
}
int main() {
// 在主线程中设置信号处理函数
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
char curtime[80];
getCurrentTime(curtime);
printf("main thread time:%s\n",curtime);
// 主线程可以进行其他操作...
sleep(2);
// 向子线程发送SIGUSR1信号
pthread_kill(thread, SIGUSR1);
pthread_join(thread, NULL);
return 0;
}
输出结果:
main thread time:2023-08-03 07:32:59
Signal 10 received in thread 140202152482560,time:2023-08-03 07:33:01
t:2
上述示例可以看出,当在主线程中通过sigaction设置SIGUSR1的信号处理函数后,创建子线程,子线程是可以继承主线程设置的信号处理函数的,并且在创建完子线程后,打印当前时间,sleep 2秒后发送SIGUSR1信号给子线程,此时子线程不会等待5秒,而是sleep了2s后立马去响应了信号处理函数。
如果希望在子线程中设置不同的信号处理函数,你需要在每个子线程中独立地调用sigaction
函数来进行设置。
示例2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
void getCurrentTime(char* curtime)
{
time_t currentTime;
struct tm* timeInfo;
char timeString[80];
// 获取当前时间
currentTime = time(NULL);
// 将时间转换为本地时间
timeInfo = localtime(¤tTime);
// 将时间格式化为字符串
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo);
strcpy(curtime,timeString);
}
void sig_handler(int signum) {
char curtime[80];
getCurrentTime(curtime);
printf("Signal %d received in thread %lu,time:%s\n", signum, pthread_self(),curtime);
}
void sig_handler_thread(int signum) {
char curtime[80];
getCurrentTime(curtime);
printf("thread:%lu Signal handle %d received,time:%s\n", pthread_self(),signum,curtime);
}
void* thread_function(void* arg) {
struct sigaction sa;
sa.sa_handler = sig_handler_thread;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
// 子线程可以进行其他操作...
int t = sleep(5);
printf("t:%d\n",t);
return NULL;
}
int main() {
// 在主线程中设置信号处理函数
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
char curtime[80];
getCurrentTime(curtime);
printf("main thread time:%s\n",curtime);
// 主线程可以进行其他操作...
sleep(2);
// 向子线程发送SIGUSR1信号
pthread_kill(thread, SIGUSR1);
pthread_join(thread, NULL);
return 0;
}
输出:
main thread time:2023-08-03 08:11:28
thread:140644435814144 Signal handle 10 received,time:2023-08-03 08:11:30
t:2
上面示例中,在主线程中设置了SIGUSR1的信号处理函数为sig_handler,又在子线程中重新设置SIGUSR1的信号处理函数为sig_handler_thread,在向子线程发送SIGUSR1信号后,子线程调用的是sig_handler_thread。
pthread_sigmask
如果想在子线程中屏蔽掉主线程设置的信号处理时,可以通过在子线程中调用pthread_sigmask对SIGUSR1信号设置BLOCK(屏蔽)。
示例3:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
void getCurrentTime(char* curtime)
{
time_t currentTime;
struct tm* timeInfo;
char timeString[80];
// 获取当前时间
currentTime = time(NULL);
// 将时间转换为本地时间
timeInfo = localtime(¤tTime);
// 将时间格式化为字符串
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo);
strcpy(curtime,timeString);
}
void sig_handler(int signum) {
char curtime[80];
getCurrentTime(curtime);
printf("Signal %d received in thread %lu,time:%s\n", signum, pthread_self(),curtime);
}
void* thread_function(void* arg) {
printf("thread:%ld is running!\n",pthread_self());
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
int t= sleep(5);
char curtime[80];
getCurrentTime(curtime);
printf("time:%s, t:%d ,thread:%ld exit!\n",curtime,t,pthread_self());
return NULL;
}
int main() {
// 在主线程中设置信号处理函数
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
char curtime[80];
getCurrentTime(curtime);
printf("main thread time:%s\n",curtime);
sleep(2);
pthread_kill(thread,SIGUSR1);
// 主线程等待子线程结束
pthread_join(thread, NULL);
return 0;
}
运行结果:
thread:140044021896960 is running!
main thread time:2023-08-03 07:59:03
time:2023-08-03 07:59:08, t:0 ,thread:140044021896960 exit!
当创建完子线程后sleep 2秒后对子线程发送SIGUSR1信号,由于子线程屏蔽了对该信号的处理,所以不会触发信号处理函数,在子线程sleep 5秒后退出。
如果设置了对信号的屏蔽字,那么子线程收到该信号号不会处理,但是如果在子线程中解除了对信号的屏蔽字,那么在解除前收到的信号,会在解除屏蔽后,触发调用信号处理函数进行处理。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
void getCurrentTime(char* curtime)
{
time_t currentTime;
struct tm* timeInfo;
char timeString[80];
// 获取当前时间
currentTime = time(NULL);
// 将时间转换为本地时间
timeInfo = localtime(¤tTime);
// 将时间格式化为字符串
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", timeInfo);
strcpy(curtime,timeString);
}
void sig_handler(int signum) {
char curtime[80];
getCurrentTime(curtime);
printf("Signal %d received in thread %lu,time:%s\n", signum, pthread_self(),curtime);
}
void* thread_function(void* arg) {
printf("thread:%ld is running!\n",pthread_self());
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
sleep(5);
pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
int t= sleep(2);
char curtime[80];
getCurrentTime(curtime);
printf("time:%s, t:%d ,thread:%ld exit!\n",curtime,t,pthread_self());
return NULL;
}
int main() {
// 在主线程中设置信号处理函数
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
char curtime[80];
getCurrentTime(curtime);
printf("main thread time:%s\n",curtime);
sleep(2);
pthread_kill(thread,SIGUSR1);
// 主线程等待子线程结束
pthread_join(thread, NULL);
return 0;
}
输出:
main thread time:2023-08-03 08:28:52
Signal 10 received in thread 139821752706816,time:2023-08-03 08:28:57
time:2023-08-03 08:28:59, t:0 ,thread:139821752706816 exit!
上面这个例子在主线程中设置了SIGUSR1的信号执行函数,创建完子线程后等待2s向子线程发送SIGUSR1信号,由于在子线程中设置了SIGUSR1的信号屏蔽字,所以线程不会触发信号处理函数,在等待5s后,解除了对此信号的屏蔽,此时立刻触发了信号处理函数!