1,背景说明:
1,信号处理函数install在主进程
2,主进程执行initFunc函数,循环执行。
void InitFunc()
{
pthread_mutex_init(&t_Test.comm_mutex,NULL);
pthread_cond_init(&t_Test.comm_cond,NULL);
sleep(1);
while(true)
{
printf("wait unlock !!!!! id = %d\n",pthread_self());
sleep(1);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
pthread_mutex_lock(&t_Test.comm_mutex);
int pid = getpid();
printf("init pid = %d\n",pid);
int ret = pthread_cond_timedwait(&t_Test.comm_cond,&t_Test.comm_mutex,&ts);
pthread_mutex_unlock(&t_Test.comm_mutex);
if(ret == 0)
{
printf("received brocast signal\n");
is_exit_flag = true;
break;
}
else if(ret == ETIMEDOUT)
{
printf("timeout continue\n");
continue;
}
else
{
printf("unavaild message\n");
}
}
}
3,此时信号处理函数收到退出信号执行ExitFunc,发现无法退出。
void ExitFunc()
{
printf("start exit\n");
static int count = 0;
printf("send brocast signal %d\n",count);
pthread_mutex_lock(&t_Test.comm_mutex);
pthread_cond_broadcast(&t_Test.comm_cond);
pthread_mutex_unlock(&t_Test.comm_mutex);
count++;
while(is_exit_flag == false)
{
int pid = pthread_self();
printf("wait is_exit_flag relex true!!!! exit pid = %d\n",pid);
sleep(1);
}
}
2,问题原因
代码原因:
当信号处理函数执行时,主进程的其他部分会被暂停,直到该信号处理函数执行完毕并返回。
这意味着在信号处理函数执行期间,主进程不会同时执行其他函数或代码。
操作系统会确保同一进程中的信号处理函数是串行执行的,以避免竞争条件和不确定性。
这也是为什么在信号处理函数中应该尽量保持简单和高效,避免长时间的阻塞和耗时操作。
状态切换:
当信号处理函数被调用时,会涉及到系统用户态和内核态之间的切换。在Linux系统中,当进程接收到一个信号时,操作系统会进行以下步骤:
-
用户态到内核态的切换:当信号发生时,进程正常的用户态执行会被中断,操作系统将控制权转移到内核态,并且在内核中执行相应的信号处理程序。
-
信号处理程序执行:在内核态,操作系统会根据进程注册的信号处理函数来执行相应的处理程序。这个处理程序可能是默认的处理方式,也可以是进程自定义的信号处理函数。
-
内核态到用户态的切换:当信号处理程序执行完毕后,操作系统将控制权重新交还给进程的用户态执行环境,进程恢复执行。
这种用户态到内核态的切换是由操作系统负责管理的,它需要保存和恢复进程的上下文信息,包括寄存器的状态、堆栈指针等,以便在切换回用户态时能够正确地恢复进程的执行现场。
在整个信号处理过程中,用户态和内核态之间的切换是由操作系统内核来管理的,对于应用程序来说,这些切换是透明的,应用程序只需要关注信号处理函数的编写和注册即可。
程序在执行信号处理函数时,把程序的执行权限交给内核态,此时内核态只执行信号处理函数,用户态的函数就无法被执行,只有在信号处理函数完成之后,切换状态到用户态,此时才能执行用户态的函数。
3,解决办法
信号处理函数中尽量减少函数调用,避免产生死循环,可以把设置某个条件变量后,再用这个条件变量处理其他业务。