我希望进程在收到退出信号时退出。所以主线程需要等待退出信号,我以前是这样写的。
int signal(int a) {
exit_flag = 0;
}
//wait in main thread
while(exit_flag){
sleep(1);
}
但它的React有点慢。所以我找到了一个像这样的新方法,
//signal function
int signal(int a) {
pthread_mutex_unlock(&mutex);
}
//main thread
pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);
我不确定这是不是正确的。我的问题是,如果这是一个合适的方式来等待出口信号,或者你可以告诉我一个更好的方法?
衷心感谢大家的回复。我知道在信号处理程序中使用互斥锁是未定义的,并且不安全。但是,如果我通过其他方法(如jrpc调用或类似的方法)发送退出消息,上面的代码是否正确?
3条答案
按热度按时间5uzkadbs1#
我的问题是,如果这是一个合适的方式来等待出口信号,或者你可以告诉我一个更好的方法?
不,是因为@AndrewHenle在his answer中描述得很好的原因。
有多种正确的方法可以做到这一点,但其中许多方法重新发明了
pause()
函数及其改进的替代方法sigsuspend()
。这两个是专门用于等待信号传递的。Glibc手册包含关于how to use them for the purpose的一节。或者,如果您的进程是多线程的,并且您只想将一个线程用于等待,那么可以使用
sigwait()
、sigwaitinfo()
andsigtimedwait()
。让整个流程等待
假设您希望整个进程停止,直到将
SIGUSR1
传递给它,然后退出。在安装信号处理程序之后,您可能会使用如下内容:关于标志的数据类型
对上面代码中的一个注解进行扩展,即使在多线程程序中,
volatile sig_atomic_t
也是足够的类型。sigsuspend()
被指定为在信号处理程序返回之后返回,并且信号处理程序直到实际发生对标志的写入之后才返回(由于易失性)。然后,由于易失性的原因,调用sigSuspend的线程必须读取处理程序写入的值,或随后写入同一变量的其他值。然而,
volatile
通常不足以保证线程安全,因此即使在这种情况下不是必需的,您也可以考虑通过使用atomic_flag
(在stdatomic.h
中声明)来回避问题和任何不确定性;这需要支持C11或更高版本。只有一个线程等待
对于多个线程中有一个在等待信号的情况,它的结构应该有很大的不同。在这种情况下,您不需要信号处理程序或标志,但应该通过
sigprocmask()
为所有线程*阻止预期的信号:这将防止在任何线程中执行预期信号的默认(或自定义)处置,并确保要等待的线程以外的线程不会使用该信号。通常,它应该在程序执行的非常早的时候完成。
然后,为了等待信号,一个线程执行以下操作:
如果你指的是一般的“信号”
如果您的意思是“信号”作为“同步”通知的通用术语,而不是C信号处理工具的练习,那么@AndrewHenle关于信号量的建议将是完美的。在这种情况下,一定要接受这个答案。
8ehkhllq2#
不,这不是**正确的。
首先,
pthread_mutex_unlock()
不是一个异步信号安全函数,不能从信号处理程序中安全地调用。其次,互斥锁由线程锁定。如果信号处理程序在与锁定互斥锁的线程不同的线程中运行,则它将can not unlock the mutex:
如果线程试图解锁尚未锁定的互斥体或解锁的互斥体,则
pthread_mutex_unlock()
的行为应如下表的Unlock When Not Owner列中所述。该表中唯一的条目是“未定义的行为”和“返回的错误”。并且您无法真正控制信号将被传递到哪个线程(至少在不编写复杂的信号处理代码的情况下是如此)。
第三,此代码
per that same table将不会安全地阻止任何类型的互斥体。该代码将死锁,继续锁定互斥锁,或者调用未定义的行为,这些行为甚至可能看起来“工作”,但会使您的程序处于未知状态,这可能会在以后导致错误。
编辑:
第四,如果信号被多次传递,对
pthread_mutex_unlock()
的多次调用将再次导致错误或未定义的行为。但有一种异步信号安全的方法可以阻止等待信号:
sem_wait()
。sem_post()
是异步信号安全的,可以在信号处理器内部安全地调用,也可以安全地多次调用--多次调用sem_post()
只允许相应数量的sem_wait()
获得信号量,但只需要一次就可以工作:请注意,在信号处理程序中调用
sem_wait()
是**不安全的。fgw7neuy3#
除了接受信号(通过
sigwait
或sigtimedwait
),我还喜欢古老的self-pipe trick:“维护管道并选择管道输入的可读性。在[Signal]处理程序中,向管道输出写入一个字节(非阻塞,以防万一)。”我还想补充一点,信号处理程序应该由
sigaction
作为SA_RESTARTABLE安装。现在,您可以安全地将信号传递与IO混合(例如,通过
select
或poll
,或者仅阻塞read
,直到该字节到达)。