导航

Signal handling in pthreads

Posted on 2011-11-29 13:28  cornflower  阅读(908)  评论(0编辑  收藏  举报

http://stackoverflow.com/questions/5282099/signal-handling-in-pthreads

I believe the core of the problem is that signals are delivered to the process as a whole, rather than individual threads. Commonly, a single thread is nominated to handle all signals; all other threads (including the main thread) need to block the signals using pthread_sigmask().

You can set the mask to block all signals, start your signal-handler-thread, unmask the signals you wish to handle, and then back in the main thread, start all the other threads you need. They will inherit the "block all signals" mask from the main thread.

Incidentally, it's time to move away from signal(3) and switch to sigaction(2), which has reliable semantics and is better standardized. (And thus more portable.)

====>but if i use pthread_kill(thread_id,signal_no) the signal i think is delivered to he specific thread.

signals are delivered to the process as a whole, but according to the pthread_kill()documentation, it ensures that the expected thread will receive the signal if the signal is not SIGSTOP,SIGCONT or SIGTERM.

 

The one problem with you code that nobody has mentioned yet is that, while signal blocking (and delivery, if you use pthread_kill or raise) are per-thread, signal handlers are per-process. This means they're a very bad mechanism for inter-thread communication, especially if your code will ever be used as library code, since it's extremely bad behavior for a library to alter the caller's signal handlers.

Also note that using signal handlers for communication between threads has sub-optimal performance compared to other methods of thread signaling like condition variables or barriers, because there's at least one additional user-kernel-user transition (when the signal handler returns).

 

Thank you for the careful investigation and your comments. You said "while signal blocking (and delivery, if you use pthread_kill or raise) are per-thread, signal handlers are per-process. " if we register the signal handler inside the thread, don't you think that it is with reference to only that thread? What is the difference between adding signal handlers (to be precise, register signal handlers) inside the main function, and inside threads? I know if we, register inside main( ) it takes care of threads too, since threads share the main( ) code. – kingsmasher1

 

no, nothing indicates that the storage for signal is or should be thread-local. You should assume that registering a signal handler has the same effect whatever the current thread is. – Sam Hocevar

As Sam said, signal handlers are process global. You cannot install per-thread signal handlers. You could try to be clever and install a signal handler that reads a function pointer with pthread_getspecific as the thread-specific handler for the signal, except that pthread_getspecific is not async-signal-safe. As far as I know, there's no async-signal-safe way to determine what thread you're in, so the only point of delivering a signal to a specific thread is generating EINTR (if SA_RESTART is omitted from sigaction flags) or stalling that thread's progress...

 

The pthread_kill() function sends the signal sig to thread, another thread in the same process as the caller. The signal is asynchronously directed to thread.

Signal dispositions are process-wide: if a signal handler is installed, the handler will be invoked in the thread thread, but if the disposition of the signal is "stop", "continue", or "terminate", this action will affect the whole process.

pthread_t pthread_self(void); get threads tids

getpid();get process’s IDs

 

多线程开发在 Linux 平台上已经有成熟的 Pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来

pthread_create
pthread_exit
pthread_join
pthread_mutex_init
pthread_mutex_destroy
pthread_mutex_lock
pthread_mutex_unlock
pthread_cond_init
pthread_cond_destroy
pthread_cond_signal
pthread_cond_broadcast
pthread_cond_wait / pthread_cond_timedwait

 

Linux 线程编程中的 5 条经验

尽量设置 recursive 属性以初始化 Linux 的互斥变量

 

互斥锁是多线程编程中基本的概念,在开发中被广泛使用。其调用次序层次清晰简单:建锁,加锁,解锁,销毁锁。但是需要注意的是,与诸如 Windows 平台的互斥变量不同,在默认情况下,Linux 下的同一线程无法对同一互斥锁进行递归加速,否则将发生死锁。

pthread_mutexattr_init(&attr);
// 设置 recursive 属性
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(theMutex,&attr);

因此,建议尽量设置 recursive 属性以初始化 Linux 的互斥锁,这样既可以解决同一线程递归加锁的问题,又可以避免很多情况下死锁的发生。这样做还有一个额外的好处,就是可以让 Windows 和 Linux 下让锁的表现统一。

 

注意 Linux 平台上触发条件变量的自动复位问题

 

 

Linux 平台上 Pthread 下的条件变量状态变化模型是这样工作的:调用 pthread_cond_signal() 释放被条件阻塞的线程时,无论存不存在被阻塞的线程,条件都将被重新复位,下一个被条件阻塞的线程将不受影响。

注意条件返回时互斥锁的解锁问题

在 Linux 调用 pthread_cond_wait 进行条件变量等待操作时,我们增加一个互斥变量参数是必要的,这是为了避免线程间的竞争和饥饿情况。但是当条件等待返回时候,需要注意的是一定不要遗漏对互斥变量进行解锁。

    Linux 平台上的 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 函数返回时,互斥锁 mutex 将处于锁定状态。因此之后如果需要对临界区数据进行重新访问,则没有必要对 mutex 就行重新加锁。但是,随之而来的问题是,每次条件等待以后需要加入一步手动的解锁操作。

 

正确处理 Linux 平台下的线程结束问题

在 Linux 平台下,当处理线程结束时需要注意的一个问题就是如何让一个线程善始善终,让其所占资源得到正确释放。在 Linux 平台默认情况下,虽然各个线程之间是相互独立的,一个线程的终止不会去通知或影响其他的线程。但是已经终止的线程的资源并不会随着线程的终止而得到释放,我们需要调用 pthread_join() 来获得另一个线程的终止状态并且释放该线程所占的资源。 Pthread_join() 函数的定义如清单 9 。

清单 9. pthread_join 函数定义

int pthread_join(pthread_t th, void **thread_return);

调用该函数的线程将挂起,等待 th 所表示的线程的结束。 thread_return 是指向线程 th 返回值的指针。需要注意的是 th 所表示的线程必须是 joinable 的,即处于非 detached(游离)状态;并且只可以有唯一的一个线程对 th 调用 pthread_join() 。如果 th 处于 detached 状态,那么对 th 的 pthread_join() 调用将返回错误。

 

The receiver's signal handler is simple and uncontroversial. It might, for instance:

  • set sig_atomic_t flags
  • call sigaddset(&signals_i_have_seen_recently, latest_sig)
  • write() a byte to a non-blocking self-pipe to break itself out of a select or pselect(or to awaken another thread directly)

all of which are signal handler safe.

In the loop body, again with most/all signals blocked, the thread then converts the newly arrived signals, perhaps broadcasting a pthread_cond_t, waking up other threads with more I/O, enqueuing a command in an application-specific thread-safe queue, whatever.

 

According to the POSIX standard all threads should appear with the same PID on the system and using pthread_sigmask you can define the signal blocking mask for every thread.

Since it is allowed to define only one signal handler per PID, I prefer to handle all signals in one thread and send pthreads_cancel if a running thread need to be cancelled. It is the preferred way against pthreads_kill since it allows to define cleanup functions for the threads.

What is the difference between sigaction and signal?

  1. The signal() function does not block other signals from arriving while the current handler is executing; sigaction() can block other signals until the current handler returns.
  2. The signal() function resets the signal action back to SIG_DFL (default) for almost all signals. This means that the signal() handler must reinstall itself as its first action. It also opens up a window of vulnerability between the time when the signal is detected and the handler is reinstalled during which if a second instance of the signal arrives, the default behaviour (usually terminate, sometimes with prejudice - aka core dump) occurs.

http://stackoverflow.com/questions/6621785/posix-pthread-programming