当进程接收到退出信号时,我希望进程退出。所以主线程需要等待退出信号,我以前这样写过。
int signal(int a) {
exit_flag = 0;
}
//wait in main thread
while(exit_flag){
sleep(1);
}但反应有点慢。所以我找到了这样一种新方法,
//signal function
int signal(int a) {
pthread_mutex_unlock(&mutex);
}
//main thread
pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);我不确定这是否正确。我的问题是,如果这是等待出口信号的首选方式,或者你可以告诉我一个更好的方法?
衷心感谢大家的回复。我知道在信号处理程序中使用互斥对象是不安全的。但是,如果我通过其他方法发送退出消息,比如jrpc调用之类的,那么上面的代码正确吗?
发布于 2022-10-21 13:19:53
我的问题是,如果这是等待出口信号的首选方式,或者你可以告诉我一个更好的方法?
不,因为@AndrewHenle在他的回答中描述得很好的原因。
有多种正确的方法可以做到这一点,但其中许多方法重新发明了pause()函数的轮子及其改进的替代sigsuspend()。这两种方法都是专门为等待信号的传递而设计的。Glibc手册包含关于如何将它们用于此目的的一节。
或者,如果您的进程是多线程的,并且只希望将一个线程用于等待,那么就会出现sigwait()、sigtimedwait()。
等待整个过程
假设您希望整个进程停止,直到一个SIGUSR1被交付给它,然后退出。安装信号处理程序后,可以使用以下内容:
// sufficient for this case even in a multi-threaded program:
volatile sig_atomic_t exit_flag;
// ...
/*
* Wait, if necessary, until a SIGUSR1 is received, then exit with status 0.
*
* Assumes that a signal handler for SIGUSR1 is already installed, that the
* handler will set variable `exit_flag` to nonzero when it runs, and that
* nothing else will modify exit_flag incompatibly.
*/
void wait_to_exit(void) {
sigset_t temp_mask, mask;
sigemptyset(&temp_mask);
sigaddset(&temp_mask, SIGUSR1);
/*
* Temporarily block the signal we plan to wait for, to ensure that we
* don't miss a signal.
*/
sigprocmask(SIG_BLOCK, &temp_mask, &mask);
// Prepare to wait for the expected signal even if it is presently blocked
sigdelset(&mask, SIGUSR1);
// if we haven't already received the signal, then block the whole process until we do
while (!exit_flag) {
sigsuspend(&mask);
}
// No need to reset any signal masks because we're about to ...
exit(0);
}关于标志的数据类型
为了扩展上述代码中的注释之一,即使在多线程程序中,volatile sig_atomic_t也是一个足够的类型。sigsuspend()被指定为在信号处理程序返回后返回,并且信号处理程序直到对标志的写入实际发生之后才会返回(因为不稳定)。然后,调用σ挂起的线程必须读取处理程序写入的值,或者随后写入同一变量的其他值,同样是因为易变性。
然而,对于线程安全来说,volatile通常是不够的,因此即使在这种情况下没有必要,您也可以通过使用atomic_flag (在stdatomic.h中声明)来考虑避开这个问题和任何关于它的不确定性;这需要对C11或更高版本的支持。
只需要等待一个线程
对于在许多等待信号的线程中有一个线程的情况,它的结构应该是非常不同的。在这种情况下,您不需要信号处理程序或标志,但是您应该通过sigprocmask()阻塞所有线程的预期信号。
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, NULL);这将防止在任何线程中执行预期信号的默认(或自定义)处理,并确保等待线程以外的线程不会消耗该信号。通常,它应该在程序执行的早期就完成。
然后,为了等待信号,线程执行以下操作:
void wait_to_exit(void) {
sigset_t mask;
int sig;
sigemptyset(&temp_mask);
sigaddset(&temp_mask, SIGUSR1);
// if we haven't already received the signal, then block this thread until we do
if (sigwait(&mask, &sig) != 0) {
// Something is terribly wrong
fputs("sigwait failed\n", stderr);
abort();
}
assert(sig == SIGUSR1);
// Terminate the process (all threads)
exit(0);
}如果你是说一个普通的“信号”
如果您的意思是“信号”作为同步通知的通用术语,而不是使用C信号处理工具,那么@AndrewHenle关于信号量的建议将是完美的。在这种情况下,一定要接受这个答案。
发布于 2022-10-21 12:31:21
不,不正确,不对。
首先,pthread_mutex_unlock()不是异步信号安全功能,不能从信号处理程序中安全地调用。
第二,互斥锁由线程锁定。如果信号处理程序运行在与具有互斥锁的线程不同的线程中,则为无法解锁互斥。
如果线程试图解锁未锁定的互斥对象或未锁定的互斥锁,则当下表的not所有者列时,
pthread_mutex_unlock()的行为应与解锁中所描述的相同。
该表中唯一的条目是“未定义的行为”和“返回的错误”。而且您无法真正控制信号将被传递到哪个线程(至少在编写复杂的信号处理代码的情况下不会如此)。
第三,这个代码
pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);对于任何类型的互斥对象,在同一张桌子上都不会安全阻塞。该代码要么死锁,要么继续锁定互斥锁,要么调用未定义的行为,这些行为甚至可能“工作”,但会使程序处于未知状态,这可能会在稍后导致错误。
编辑:
第四,如果信号被多次传递,对pthread_mutex_unlock()的多次调用将再次导致错误或未定义的行为。
但是有一种异步信号安全的方法来阻止等待信号:sem_wait()。
sem_post() 是异步信号安全的,可以从信号处理程序中安全地调用,也可以安全地多次调用--对sem_post()的多次调用将只允许对sem_wait()的相应次数调用以获得信号量,但您只需要使用一个:
//signal function
int signal(int a) {
sem_post(&sem);
}
//main thread
sem_wait(&sem);请注意,在信号处理程序中调用而不是安全。
https://stackoverflow.com/questions/74151419
复制相似问题