1. 程式人生 > >多執行緒中的訊號機制--sigwait 函式

多執行緒中的訊號機制--sigwait 函式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

在Linux的多執行緒中使用訊號機制,與在程序中使用訊號機制有著根本的區別,可以說是完全不同。在程序環境中,對訊號的處理是,先註冊訊號處理函式,當訊號非同步發生時,呼叫處理函式來處理訊號。它完全是非同步

的(我們完全不知到訊號會在程序的那個執行點到來!)。然而訊號處理函式的實現,有著許多的限制;比如有一些函式不能在訊號處理函式中呼叫;再比如一些函式read、recv等呼叫時會被非同步的訊號給中斷(interrupt),因此我們必須對在這些函式在呼叫時因為訊號而中斷的情況進行處理(判斷函式返回時 enno 是否等於 EINTR)。


但是在多執行緒中處理訊號的原則卻完全不同,它的基本原則是:將對訊號的非同步處理,轉換成同步處理,也就是說用一個執行緒專門的來“同步等待”訊號的到來,而其它的執行緒可以完全不被該訊號中斷/打斷(interrupt)。這樣就在相當程度上簡化了在多執行緒環境中對訊號的處理。而且可以保證其它的執行緒不受訊號的影響。這樣我們對訊號就可以完全預測
,因為它不再是非同步的,而是同步的
我們完全知道訊號會在哪個執行緒中的哪個執行點到來而被處理!。而同步的程式設計模式總是比非同步的程式設計模式簡單。其實多執行緒相比於多程序的其中一個優點就是:多執行緒可以將程序中非同步的東西轉換成同步的來處理。

1. sigwait函式:
  1. sigwait - wait for a signal

  2. #include <signal.h>
  3. int sigwait(const sigset_t *set, int *sig);

  4. Description
  5. The sigwait() function suspends execution of the calling thread until the delivery of one 
  6. of the signals specified in the signal set set. The function accepts the signal (removes 
  7. it from the pending list of signals), and returns the signal number insig.

  8. The operation of sigwait() is the same as sigwaitinfo(2), except that:

  9. * sigwait() only returns the signal number, rather than a siginfo_t structure describing 
  10.   the signal.
  11. * The return values of the two functions are different.

  12. Return Value

  13. On success, sigwait() returns 0. On error, it returns a positive error number.
從上面的man sigwait的描述中,我們知道:sigwait是同步的等待訊號的到來,而不是像程序中那樣是非同步的等待訊號的到來。sigwait函式使用一個訊號集作為他的引數,並且在集合中的任一個訊號發生時返回該訊號值,解除阻塞,然後可以針對該訊號進行一些相應的處理。 2. 記住:      在多執行緒程式碼中,總是使用sigwait或者sigwaitinfo或者sigtimedwait等函式來處理訊號。      而不是signal或者sigaction等函式。因為在一個執行緒中呼叫signal或者sigaction等函式會改變所以執行緒中的      訊號處理函式。而不是僅僅改變呼叫signal/sigaction的那個執行緒的訊號處理函式。 3. pthread_sigmask函式:     每個執行緒均有自己的訊號遮蔽集(訊號掩碼),可以使用pthread_sigmask函式來遮蔽某個執行緒對某些訊號的    響應處理,僅留下需要 處理該訊號的執行緒來處理指定的訊號。實現方式是:利用執行緒訊號遮蔽集的繼承關係   (在主程序中對sigmask進行設定後,主程序創建出來的執行緒將繼承主程序的掩碼
  1. pthread_sigmask - examine and change mask of blocked signals

  2. #include <signal.h>
  3. int pthread_sigmask(inthow, const sigset_t *set, sigset_t *oldset);

  4. Compile and link with -pthread.

  5. DESCRIPTION
  6. The pthread_sigmask() function is just like sigprocmask(2), with the difference thatits use
  7. in multithreaded programs is explicitly specified by POSIX.1-2001.
  8. Other differences are noted in this page.
  9. For a description of the arguments and operation of this function, see sigprocmask(2).

  10. RETURN VALUE
  11. On success, pthread_sigmask() returns 0; on error, it returns an error number.
  12. NOTES
  13. A new thread inherits a copy of its creator's signal mask.

  14. (from man sigprocmask: )

  15. The behavior of the call is dependent on the value of how, as follows.
  16. SIG_BLOCK
  17. The set of blocked signals is the union of the current set and the set argument.
  18. SIG_UNBLOCK
  19. The signals in set are removed from the current set of blocked signals. It is permissible 
  20. to attempt to unblock a signal which is not blocked.
  21. SIG_SETMASK
  22. The set of blocked signals is set to the argument set.

  23. If oldset is non-NULL, the previous value of the signal mask is stored inoldset.

  24. If set is NULL, then the signal mask is unchanged (i.e.,how is ignored), but the current 
  25. value of the signal mask is nevertheless returned inoldset (if it is not NULL).
4. pthread_kill函式:    在多執行緒程式中,一個執行緒可以使用pthread_kill對同一個程序中指定的執行緒(包括自己)傳送訊號。注意在多執行緒中     一般不使用kill函式傳送訊號,因為kill是對程序傳送訊號,結果是:正在執行的執行緒會處理該訊號,如果該執行緒沒有  註冊訊號處理函式,那麼會導致整個程序退出。
  1. #include <signal.h>
  2. int pthread_kill(pthread_tthread, intsig);

  3. Compile and link with -pthread.

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

  7. If sig is 0, then no signal is sent, but error checking is still performed; this can be 
  8. used to check for the existence of a thread ID.

  9. RETURN VALUE
  10. On success, pthread_kill() returns 0; on error, it returns an error number, and no signal 
  11. is sent.

  12. ERRORS
  13. ESRCH No thread with the ID thread could be found.
  14. EINVAL An invalid signal was specified.
5. 記住:呼叫sigwait同步等待的訊號必須在呼叫執行緒中被遮蔽,並且通常應該在所有的執行緒中被遮蔽(這樣可以保證訊號絕不會被送到除了呼叫sigwait的任何其它執行緒),這是通過利用訊號掩碼的繼承關係來達到的。 (The semantics of sigwait require that all threads (including the thread calling sigwait) have the signal masked, for   reliable operation. Otherwise, a signal that arrives not blocked in sigwait might be  delivered to another thread. ) 6. 程式碼示例:(from man pthread_sigmask)
  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <errno.h>

  7. /* Simpleerror handling functions*/

  8. #define handle_error_en(en, msg)\
  9.         do { errno= en; perror(msg);exit(EXIT_FAILURE);}while(0)

  10. static void *
    1. sig_thread(void*arg)
  11. {
  12.       sigset_t *set=(sigset_t*) arg;
  13.       int s, sig;

  14.       for (;;){
  15.             s = sigwait(set,&sig);
  16.             if (s != 0)
  17.                   handle_error_en(s,"sigwait");
  18.             printf("Signal handling thread got signal %d\n", sig);
  19.       }
  20. }

  21. int
    1. main(int argc, char*argv[])
  22. {
  23.       pthread_t thread;
  24.       sigset_t set;
  25.       int s;

  26.       /* 
  27.          Block SIGINT; other threads created by main() will inherit
  28.          a copy of the signal mask. 
  29.        */
  30.       sigemptyset(&set);
  31.       sigaddset(&set, SIGQUIT);
  32.       sigaddset(&set, SIGUSR1);
  33.       s = pthread_sigmask(SIG_BLOCK,&set,NULL);
  34.       if (s!= 0)
  35.             handle_error_en(s,"pthread_sigmask");
  36.       s = pthread_create(&thread,NULL,&sig_thread,(void*)&set);
  37.       if (s!= 0)
  38.             handle_error_en(s,"pthread_create");
  39.       /* 
  40.         Main thread carries on to create other threads and/ordo
  41.         other work 
  42.        */
  43.       pause();/* Dummy pause so we can test program*/
  44.       return 0;
  45. }
編譯執行情況:
  1. [email protected]:~/pthread/learnthread$ gcc-Wall-pthread-o pthread_sigmask pthread_sigmask.c
  2. [email protected]:~/pthread/learnthread$./pthread_sigmask&
  3. [1] 4170
  4. [email protected]:~/pthread/learnthread$ kill-QUIT%1
  5. [email protected]:~/pthread/learnthread$ Signal handling thread got signal 3

  6. [email protected]u:~/pthread/learnthread$ kill-USR1%1
  7. [email protected]:~/pthread/learnthread$ Signal handling thread got signal 10

  8. [email protected]:~/pthread/learnthread$ kill-TERM%1
  9. [email protected]:~/pthread/learnthread$
  10. [1]+ Terminated./pthread_sigmask
  11. [email protected]:~/pthread/learnthread$
這個例子演示了:通過在主執行緒中阻塞一些訊號,其它的執行緒會繼承訊號掩碼,然後專門用一個執行緒使用sigwait函式來同步的處理訊號,使其它的執行緒不受到訊號的影響。

#include <pthread.h>#include <stdio.h>#include <sys/signal.h>#define NUMTHREADS 3void sighand(int signo);void *threadfunc(void *parm)pthread_t             tid = pthread_self(); int                   rc; printf("Thread %u entered\n", tid); rc = sleep(3); printf("Thread %u did not get expected results! rc=%d\n", tid, rc); return NULL;}void *threadmasked(void *parm)pthread_t             tid = pthread_self(); sigset_t              mask; int                   rc; printf("Masked thread %lu entered\n", tid); sigfillset(&mask); /* Mask all allowed signals */// sigemptyset(&mask); rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); if (rc != 0) {  printf("%d, %s\n", rc, strerror(rc));  return NULL; } rc = sleep(1); if (rc != 0) {  printf("Masked thread %lu did not get expected results! ""rc=%d \n",tid, rc);  return NULL; }// sigwait(&mask,&rc); printf("Masked thread %lu completed masked work\n",tid); return NULL;}int main(int argc, char **argv)int                     rc; int                     i; struct sigaction        actions; pthread_t               threads[NUMTHREADS]; pthread_t               maskedthreads[NUMTHREADS]; printf("Enter Testcase - %s\n", argv[0]); printf("Set up the alarm handler for the process\n"); memset(&actions, 0, sizeof(actions)); sigemptyset(&actions.sa_mask); actions.sa_flags = 0; actions.sa_handler = sighand; rc = sigaction(SIGALRM,&actions,NULL); printf("Create masked and unmasked threads\n"); for(i=0; i<NUMTHREADS; ++i) {  rc = pthread_create(&threads[i], NULL, threadfunc, NULL);  if (rc != 0)  {   printf("%d, %s\n", rc, strerror(rc));   return -1;  }  rc = pthread_create(&maskedthreads[i], NULL, threadmasked, NULL);  if (rc != 0)  {   printf("%d, %s\n", rc, strerror(rc));   return -1;  } } sleep(5); printf("Send a signal to masked and unmasked threads\n"); for(i=0; i<NUMTHREADS; ++i) {  rc = pthread_kill(threads[i], SIGALRM);  rc = pthread_kill(maskedthreads[i], SIGALRM); } printf("Wait for masked and unmasked threads to complete\n"); for(i=0; i<NUMTHREADS; ++i) {  rc = pthread_join(threads[i], NULL);  rc = pthread_join(maskedthreads[i], NULL); } printf("Main completed\n"); return 0;}void sighand(int signo)pthread_t             tid = pthread_self(); printf("Thread %lu in signal handler\n",tid); return;}
(ps:由上面的幾篇文章可知,執行緒中的訊號處理機制和程序中的訊號處理機制是不同的,或者說是完全不同的,一般在多執行緒中使用訊號的,或單獨啟動一個執行緒類處理訊號,在主程序中傳送訊號,在主程序中使用了pthread_mask()函式來處理訊號遮蔽資訊,這個時候在主程序中使用kill,這個時候執行緒也是可以接受到訊號的原因是pthread_mask繼承了主程序的訊號遮蔽資訊,這個時候也是可以在使用pthread_kill給相應的執行緒傳送相應的訊號,但是在有的程式中,線上程中使用pthread_mask,這個使用就必須使用pthread_kill來發送訊號了;但是在程序中的訊號機制沒有這麼複雜,程序中只需要註冊一個函式,就靜靜的等待訊號處理。不過在程序中使用的方法也是可以線上程中使用的)

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述