2013年11月1日 星期五

[轉錄] thread's signal mask

就跟傳統的 process 一樣,每個 thread 都有一個 signal mask,用來指定那些非同步 signal 會被 thread 處理,稱為 unblocked signals,那些 signal 不會被 thread 處理,稱為 blocked signals。根據 kernel 的設計,child process 中的第一個 thread 會繼承 parent process 中呼叫 fork 的 thread 的 signal mask,其它 thread 則會繼承呼叫 pthread_create 的 thread 的 signal mask。然後在 thread 再利用 pthread_sigmask 來調整 signal 是 blocked or unblocked。
當一個非同步產生的 signal 送達某個 process 時,它將由 process 中某個 thread 來處理,系統將會根據每個 thread 的 signal mask 來決定誰是選中的 thread 。如果有超過一個以上的 thread,它們的 signal mask 對 signal 來說都是 unblocked,系統將任意的幫你選一個。雖然你可以經由設定 signal mask 來影響系統選 thread 的過程,但無法直接指定某個 thread 來處理某個指定的 signal。
下面演示 pthread_sigmask 的範例,每隔二秒發出 SIGALRM 一次,由這個範例得到幾個結論。
  1. 預設的 signal mask = 0,也就是所有的 signal 都是 unblocked。
  2. signal 對 threads 的選擇都是以 thread 產生的順序為則準,所以每次都是第一個 threads 被選中。
  3. main 算是第一個 thread,如果沒有在 main 中使用 pthread_sigmask 來 block signal,則所有的 signal 都會由 main thread 來處理。
  4. main 中使用 pthread_sigmask 要注意,如果在 pthread_create 前使用,後面的 threads 會繼承變更後的 signal mask。



 Signal handling with multiple threads in Linux


Q:
In Linux, what happens when a program (that possibly has multiple threads) receives a signal, like SIGTERM or SIGHUP?
Which thread intercepts the signal? Can multiple threads get the same signal? Is there a special thread dedicated entirely to handling signals? If not, what happens inside the thread that is to handle the signal? How does the execution resume after the signal handler routine finishes?


ANS:

pthreads(7) describes that POSIX.1 requires all threads in a process share attributes, including:
  • signal dispositions
POSIX.1 also requires some attributes to be distinct for each thread, including:
  • signal mask (pthread_sigmask(3))
  • alternate signal stack (sigaltstack(2))
The Linux kernel's complete_signal() routine has the following code block -- the comments are quite useful:
    /*
     * Now find a thread we can wake up to take the signal off the queue.
     *
     * If the main thread wants the signal, it gets first crack.
     * Probably the least surprising to the average bear.
     */
    if (wants_signal(sig, p))
            t = p;
    else if (!group || thread_group_empty(p))
            /*
             * There is just one thread and it does not need to be woken.
             * It will dequeue unblocked signals before it runs again.
             */
            return;
    else {
            /*
             * Otherwise try to find a suitable thread.
             */
            t = signal->curr_target;
            while (!wants_signal(sig, t)) {
                    t = next_thread(t);
                    if (t == signal->curr_target)
                            /*
                             * No thread needs to be woken.
                             * Any eligible threads will see
                             * the signal in the queue soon.
                             */
                            return;
            }
            signal->curr_target = t;
    }

    /*
     * Found a killable thread.  If the signal will be fatal,
     * then start taking the whole group down immediately.
     */
    if (sig_fatal(p, sig) &&
        !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
        !sigismember(&t->real_blocked, sig) &&
        (sig == SIGKILL || !t->ptrace)) {
            /*
             * This signal will be fatal to the whole group.
             */
So, you see that you are in charge of where signals are delivered:
If your process has set a signal's disposition to SIG_IGN or SIG_DFL, then the signal is ignored (or default -- kill, core, or ignore) for all threads.
If your process has set a signal's disposition to a specific handler routine, then you can control which thread will receive the signals by manipulating specific thread signal masks using pthread_sigmask(3). You can nominate one thread to manage them all, or create one thread per signal, or any mixture of these options for specific signals, or you rely on the Linux kernel's current default behavior of delivering the signal to the main thread.
Some signals, however, are special:
   A signal may be generated (and thus pending) for a process as
   a whole (e.g., when sent using kill(2)) or for a specific
   thread (e.g., certain signals, such as SIGSEGV and SIGFPE,
   generated as a consequence of executing a specific machine-
   language instruction are thread directed, as are signals
   targeted at a specific thread using pthread_kill(3)).  A
   process-directed signal may be delivered to any one of the
   threads that does not currently have the signal blocked.  If
   more than one of the threads has the signal unblocked, then
   the kernel chooses an arbitrary thread to which to deliver
   the signal.




沒有留言: