在拥有带有信号处理程序的多线程程序时,有没有办法确保原子性?
Is there a way to ensure atomicity while having a multithreaded program with signal handlers?
如果我有这样的程序(伪代码):
mutex_lock;
func() {
lock(mutex_lock);
// Some code (long enough to make a
// race condition if no proper synchronisation
// is available). We also going to call a signal,
// say, SIGINT, through (ctrl-c), while we are in
// the range of locking and unlocking the lock.
unlock(mutex_lock);
}
sig_handler_func(sig) {
// Say, we are handling SIGINT (ctrl-c) signal
// And we need to call func from here too.
if (sig == SIGINT) {
func();
}
}
main() {
// Calling func from main
func();
}
那么当尝试获取 func 中的锁时会出现死锁,而它已经从 'main' 调用者处获取。我想知道是否有一种方法可以暂停 C 语言中的信号处理,具体来说,在这个例子中,当进入 func 并获取锁时,恢复信号处理并在退出 func 时调用处理程序。
你想要pthread_sigmask
,sigprocmask
的多线程版本
下面是一些示例伪代码:
int
main(void)
{
sigset_t omask;
sigset_t nmask;
// add as many signals as you want to the mask ...
sigemptyset(&nmask);
sigaddset(&nmask,SIGINT);
// [temporarily] block signals
pthread_sigmask(SIG_BLOCK,&nmask,&omask);
// call function safely
func();
// restore signal mask
pthread_sigmask(SIG_SETMASK,&omask,NULL);
// pending signals should occur now ...
}
我不完全确定,但是,您可能需要使用 pthread_sigmask
来阻止除 一个 线程之外的所有信号,并且仅从该线程执行上述操作.
另外,如果我不说我会重构你的代码,那我就失职了。 [除此之外] 在信号处理程序中可以做的事情的数量是有限的(例如没有 malloc
、没有 printf
等)
将一个线程专用于信号处理并让它执行 sigsetjmp
并且信号处理程序执行 siglongjmp
。
或者让信号处理程序设置一个在基本级别监视的可变全局(例如 signal_occurred
)。
因此,您在信号处理程序中所做的所有 "heavy lifting" 都可以从基本任务级别完成,您可以在其中执行任何操作。
你需要两把锁。一种用于您的 func()
,另一种用于保护进程的信号掩码。
您还必须使屏蔽和取消屏蔽信号原子化:
static pthread_mutex_t mask_mutex = PTHREAD_MUTEX_INITIALIZER;
sigset_t old_set;
sigset_t new_set;
sigemptyset( &new_set );
sigaddset( &new_set, SIGINT );
pthread_mutex_lock( &mask_mutex );
pthread_sigmask( SIG_BLOCK, &new_mask, &old_mask );
func();
pthread_sigmask( SIG_SETMASK, &old_mask, NULL );
pthread_mutex_unlock( &mask_mutex );
pthread_sigmask()
周围没有锁,线程可能会在执行重叠时破坏进程 sigmask。
为了安全起见,您可以在这种未定义的情况下使用测试互斥 函数(trylock)。有了这个,你也不一定需要阻止。
如果我有这样的程序(伪代码):
mutex_lock;
func() {
lock(mutex_lock);
// Some code (long enough to make a
// race condition if no proper synchronisation
// is available). We also going to call a signal,
// say, SIGINT, through (ctrl-c), while we are in
// the range of locking and unlocking the lock.
unlock(mutex_lock);
}
sig_handler_func(sig) {
// Say, we are handling SIGINT (ctrl-c) signal
// And we need to call func from here too.
if (sig == SIGINT) {
func();
}
}
main() {
// Calling func from main
func();
}
那么当尝试获取 func 中的锁时会出现死锁,而它已经从 'main' 调用者处获取。我想知道是否有一种方法可以暂停 C 语言中的信号处理,具体来说,在这个例子中,当进入 func 并获取锁时,恢复信号处理并在退出 func 时调用处理程序。
你想要pthread_sigmask
,sigprocmask
下面是一些示例伪代码:
int
main(void)
{
sigset_t omask;
sigset_t nmask;
// add as many signals as you want to the mask ...
sigemptyset(&nmask);
sigaddset(&nmask,SIGINT);
// [temporarily] block signals
pthread_sigmask(SIG_BLOCK,&nmask,&omask);
// call function safely
func();
// restore signal mask
pthread_sigmask(SIG_SETMASK,&omask,NULL);
// pending signals should occur now ...
}
我不完全确定,但是,您可能需要使用 pthread_sigmask
来阻止除 一个 线程之外的所有信号,并且仅从该线程执行上述操作.
另外,如果我不说我会重构你的代码,那我就失职了。 [除此之外] 在信号处理程序中可以做的事情的数量是有限的(例如没有 malloc
、没有 printf
等)
将一个线程专用于信号处理并让它执行 sigsetjmp
并且信号处理程序执行 siglongjmp
。
或者让信号处理程序设置一个在基本级别监视的可变全局(例如 signal_occurred
)。
因此,您在信号处理程序中所做的所有 "heavy lifting" 都可以从基本任务级别完成,您可以在其中执行任何操作。
你需要两把锁。一种用于您的 func()
,另一种用于保护进程的信号掩码。
您还必须使屏蔽和取消屏蔽信号原子化:
static pthread_mutex_t mask_mutex = PTHREAD_MUTEX_INITIALIZER;
sigset_t old_set;
sigset_t new_set;
sigemptyset( &new_set );
sigaddset( &new_set, SIGINT );
pthread_mutex_lock( &mask_mutex );
pthread_sigmask( SIG_BLOCK, &new_mask, &old_mask );
func();
pthread_sigmask( SIG_SETMASK, &old_mask, NULL );
pthread_mutex_unlock( &mask_mutex );
pthread_sigmask()
周围没有锁,线程可能会在执行重叠时破坏进程 sigmask。
为了安全起见,您可以在这种未定义的情况下使用测试互斥 函数(trylock)。有了这个,你也不一定需要阻止。