歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> UNIX環境高級編程:線程和信號

UNIX環境高級編程:線程和信號

日期:2017/3/3 15:19:38   编辑:Unix基礎知識

每個線程都有自己的信號屏蔽字,但是信號的處理是進程中所有線程共享的。這意味著盡管單個線程可以阻止某些信號,但當線程修改了與某個信號相關的處理行為以後,所有的線程都必須共享這個處理行為的改變。這樣如果一個信號選擇忽略某個信號,而其他的線程可以恢復信號的默認處理行為,或者為信號設置一個新的處理程序,從而可以撤銷上述線程的信號選擇。

進程中的信號是送到單個線程的,如果信號與硬件故障或者計時器超時有關,該信號就被發送到引起該事件的線程中去,而其他的信號則被發送到任意一個線程。

sigprocmask的行為在多線程的進程中沒有定義,線程必須使用pthread_sigmask。

[cpp] view plaincopyprint?

01.int pthread_sigmask(int how,const sigset_t * newmask, sigset_t * oldmask);//返回值:若成功則返回0,否則返回錯誤編號

int pthread_sigmask(int how,const sigset_t * newmask, sigset_t * oldmask);//返回值:若成功則返回0,否則返回錯誤編號

pthread_sigmask函數與sigprocmask函數基本相同,除了pthread_sigmask工作在線程中,並且失敗時返回錯誤碼,而不像sigprocmask中那樣設置errno並返回-1.

線程可以通過調用sigwait等待一個或多個信號發生:

[cpp] view plaincopyprint?

01.int sigwait(const sigset_t * set, int * signop);//返回值:若成功則返回0,否則返回錯誤編號

int sigwait(const sigset_t * set, int * signop);//返回值:若成功則返回0,否則返回錯誤編號 set參數指出了線程等待的信號集,signop指向的整數將作為返回值,表明發送信號的編號(即SIGINT對應2號)。

注意:sigwait函數所等待的信號集裡的信號在之前必須被阻塞(即等待的信號需要添加到信號屏蔽字裡面)。

sigwait函數將阻塞調用它的線程,直到收到它等待的信號發生了,然後sigwait將其從未決隊列中取出(因為被阻塞了,所以肯定是未決了),但是有一點需要注意的是:它從未決隊列取出之後,並不影響那個被取出的信號原來被阻塞的狀態(即原來的信號屏蔽字不會改變的)。

它所做的工作只有兩個:

等待它所等待的信號;

如果所等待的信號產生了,則將其從未決隊列中移出來

如果多個線程調用sigwait時,且等待的是同一個信號,這時就會出現線程阻塞。當信號遞送的時候,只有一個線程可以從sigwait中返回。如果信號被捕獲(例如進程通過使用sigaction建立了一個信號處理程序)而且線程正在sigwait調用中等待同一個信號,那麼則時候由操作系統實現來決定以何種方式遞送信號。在這種情況下,操作系統實現可以讓sigwait返回,也可以激活信號處理程序,但不能出現兩者皆可的情況。(下面有代碼驗證,驗證結果是:在Ubuntu 10.04系統下,sigwait的優先級高一些)

要把信號發送到進程,可以調用kill;要把信號發送到線程,可以調用pthread_kill:

[cpp] view plaincopyprint?

01.#include <signal.h>

02.int pthread_kill(pthread_t thread,int signo);//返回值:若成功則返回0,否則返回錯誤編號。 線程不存在:ESRCH 信號不合法:EINVAL

#include <signal.h>

int pthread_kill(pthread_t thread,int signo);//返回值:若成功則返回0,否則返回錯誤編號。 線程不存在:ESRCH 信號不合法:EINVAL

可以傳一個0值的signo來檢查線程是否存在。如果信號的默認處理動作是終止進程,那麼把信號傳遞給某個線程仍然會殺掉整個進程,所以我們用0號信號,這是一個保留信號,一個作用是用來判斷線程是否還活著的。

kill()向進程發送信號,由哪個線程處理該信號是未知的。可能發生的情況是,進程本身屏蔽了該信號,而某個線程沒有屏蔽改信號,進而該線程處理了該信號。

注意鬧鐘定時器是進程資源,並且所有的線程共享相同的alarm。所以進程中的多個線程不可能不干擾(或互不合作)的使用鬧鐘定時器。

示例代碼:

[cpp] view plaincopyprint?
01.#include <stdio.h>
02.#include <pthread.h>
03.#include <unistd.h>
04.#include <stdlib.h>
05.#include <signal.h>
06.
07.void sig_thread_func(int sig)
08.{
09. printf("sig_thread_func : sig = %d\n", sig);
10.}
11.void sig_func(int sig)
12.{
13. printf("sig_func : sig = %d\n",sig);
14.}
15.
16.void *func1(void *arg)
17.{
18. signal(SIGUSR1, sig_thread_func); //線程1先運行,設置了signal
19.
20. sigset_t set;
21. sigfillset(&set);
22. sigdelset(&set, SIGUSR1);
23. pthread_sigmask(SIG_SETMASK, &set, NULL);//線程1屏蔽了除了SIGUSR1外的所有信號
24.
25. printf("pthread 1 run\n");
26. int i;
27. for(i = 0; i < 7; ++i)
28. {
29. printf("1...\n");
30. sleep(1);
31. }
32. return 0;
33.}
34.void *func2(void *arg)
35.{
36. printf("pthread 2 run\n");
37. int i;
38. for(i = 0; i < 7; ++i)
39. {
40. printf("2...\n");
41. sleep(1);
42. }
43. return 0;
44.}
45.
46.int main()
47.{
48. pthread_t tid1, tid2;
49. pthread_create(&tid1, NULL, func1, NULL);
50. pthread_create(&tid2, NULL, func2, NULL);
51.
52. sleep(1);
53. signal(SIGUSR1, sig_func); //覆蓋了線程1設置的signal
54.
55. //向線程1發送SIGUSR1,SIGUSR2
56. sleep(1);
57. pthread_kill(tid1, SIGUSR1);//調動handler
58. sleep(1);
59. pthread_kill(tid1, SIGUSR2);//屏蔽了,無響應
60.
61. //向線程2發送SIGUSR1,SIGUSR2
62. sleep(1);
63. pthread_kill(tid2, SIGUSR1);//調用handler
64. sleep(1);
65. //pthread_kill(tid2, SIGUSR2);//會終止進程,是進程!
66.
67. sigset_t set;
68. sigfillset(&set);
69. sigprocmask(SIG_SETMASK, &set, NULL);//進程屏蔽了所有信號
70.
71. sleep(1);
72. kill(getpid(), SIGUSR1);//調動handler?其實是線程1響應的
73.
74. pthread_join(tid1, NULL);
75. pthread_join(tid2, NULL);
76.
77. return 0;
78.}

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sig_thread_func(int sig)
{
printf("sig_thread_func : sig = %d\n", sig);
}
void sig_func(int sig)
{
printf("sig_func : sig = %d\n",sig);
}
void *func1(void *arg)
{
signal(SIGUSR1, sig_thread_func); //線程1先運行,設置了signal
sigset_t set;
sigfillset(&set);
sigdelset(&set, SIGUSR1);
pthread_sigmask(SIG_SETMASK, &set, NULL);//線程1屏蔽了除了SIGUSR1外的所有信號
printf("pthread 1 run\n");
int i;
for(i = 0; i < 7; ++i)
{
printf("1...\n");
sleep(1);
}
return 0;
}
void *func2(void *arg)
{
printf("pthread 2 run\n");
int i;
for(i = 0; i < 7; ++i)
{
printf("2...\n");
sleep(1);
}
return 0;
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, func1, NULL);
pthread_create(&tid2, NULL, func2, NULL);
sleep(1);
signal(SIGUSR1, sig_func); //覆蓋了線程1設置的signal
//向線程1發送SIGUSR1,SIGUSR2
sleep(1);
pthread_kill(tid1, SIGUSR1);//調動handler
sleep(1);
pthread_kill(tid1, SIGUSR2);//屏蔽了,無響應
//向線程2發送SIGUSR1,SIGUSR2
sleep(1);
pthread_kill(tid2, SIGUSR1);//調用handler
sleep(1);
//pthread_kill(tid2, SIGUSR2);//會終止進程,是進程!
sigset_t set;
sigfillset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);//進程屏蔽了所有信號
sleep(1);
kill(getpid(), SIGUSR1);//調動handler?其實是線程1響應的
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}

運行結果:

[cpp] view plaincopyprint?
01.huangcheng@ubuntu:~$ ./a.out
02.pthread 2 run
03.2...
04.pthread 1 run
05.1...
06.2...
07.1...
08.2...
09.sig_func : sig = 10
10.1...
11.2...
12.1...
13.sig_func : sig = 10
14.2...
15.1...
16.2...
17.1...
18.2...
19.sig_func : sig = 10
20.1...
huangcheng@ubuntu:~$ ./a.out
pthread 2 run
2...
pthread 1 run
1...
2...
1...
2...
sig_func : sig = 10
1...
2...
1...
sig_func : sig = 10
2...
1...
2...
1...
2...
sig_func : sig = 10
1...

示例代碼2:

[cpp] view plaincopyprint?
01.#include<stdio.h>
02.#include<pthread.h>
03.#include<signal.h>
04.
05.static void sig_alrm(int signo);
06.static void sig_init(int signo);
07.int
08.main()
09.{
10. sigset_t set;
11. int sig;
12. sigemptyset(&set);
13. sigaddset(&set, SIGALRM);
14. pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信號
15.
16. signal(SIGALRM, sig_alrm);
17. signal(SIGINT, sig_init);
18. sigwait(&set, &sig);//sigwait只是從未決隊列中刪除該信號,並不改變信號掩碼。也就是,當sigwait函數返回,它監聽的信號依舊被阻塞。
19. switch(sig){
20. case 14:
21. printf("sigwait, receive signal SIGALRM\n");
22. /*do the job when catch the sigwait*/
23. break;
24. default:
25. break;
26. }
27. // sigdelset(&set, SIGALRM);
28. // pthread_sigmask(SIG_SETMASK, &set, NULL);
29.
30. for(;;)
31. {}
32. return 0;
33.}
34.
35.static void
36.sig_alrm(int signo)
37.{
38. printf("after sigwait, catch SIGALRM\n");
39. fflush(stdout);
40. return ;
41.}
42.
43.static void
44.sig_init(int signo)
45.{
46. printf("catch SIGINT\n");
47. return ;
48.}
#include<stdio.h>
#include<pthread.h>
#include<signal.h>
static void sig_alrm(int signo);
static void sig_init(int signo);
int
main()
{
sigset_t set;
int sig;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信號
signal(SIGALRM, sig_alrm);
signal(SIGINT, sig_init);
sigwait(&set, &sig);//sigwait只是從未決隊列中刪除該信號,並不改變信號掩碼。也就是,當sigwait函數返回,它監聽的信號依舊被阻塞。
switch(sig){
case 14:
printf("sigwait, receive signal SIGALRM\n");
/*do the job when catch the sigwait*/
break;
default:
break;
}
// sigdelset(&set, SIGALRM);
// pthread_sigmask(SIG_SETMASK, &set, NULL);
for(;;)
{}
return 0;
}
static void
sig_alrm(int signo)
{
printf("after sigwait, catch SIGALRM\n");
fflush(stdout);
return ;
}
static void
sig_init(int signo)
{
printf("catch SIGINT\n");
return ;
}

執行命令:

[cpp] view plaincopyprint?
01.第一個終端:
02.huangcheng@ubuntu:~$ ./a.out
03.第二個終端:
04.huangcheng@ubuntu:~$ kill -2 2682
05.huangcheng@ubuntu:~$ kill -2 2682
06.huangcheng@ubuntu:~$ kill -14 2682
07.huangcheng@ubuntu:~$ kill -14 2682
08.huangcheng@ubuntu:~$ kill -14 2682
09.huangcheng@ubuntu:~$ kill -14 2682
第一個終端:
huangcheng@ubuntu:~$ ./a.out
第二個終端:
huangcheng@ubuntu:~$ kill -2 2682
huangcheng@ubuntu:~$ kill -2 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682
huangcheng@ubuntu:~$ kill -14 2682

執行結果:

[cpp] view plaincopyprint?

01.huangcheng@ubuntu:~$ ./a.out

02.catch SIGINT

03.catch SIGINT

04.sigwait, receive signal SIGALRM

huangcheng@ubuntu:~$ ./a.out

catch SIGINT

catch SIGINT

sigwait, receive signal SIGALRM結果說明:sigwait不會改變屏蔽字,僅僅是從未決隊列中刪除其等到的信號。

示例代碼3:

[cpp] view plaincopyprint?
01.#include<stdio.h>
02.#include<pthread.h>
03.#include<signal.h>
04.
05.static void sig_alrm(int signo);
06.static void sig_init(int signo);
07.int
08.main()
09.{
10. sigset_t set;
11. int sig;
12. sigemptyset(&set);
13. sigaddset(&set, SIGALRM);
14. pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信號
15.
16. signal(SIGALRM, sig_alrm);
17. signal(SIGINT, sig_init);
18. sigwait(&set, &sig);//sigwait只是從未決隊列中刪除該信號,並不改變信號掩碼。也就是,當sigwait函數返回,它監聽的信號依舊被阻塞。
19. switch(sig){
20. case 14:
21. printf("sigwait, receive signal SIGALRM\n");
22. /*do the job when catch the sigwait*/
23. break;
24. default:
25. break;
26. }
27. sigdelset(&set, SIGALRM);
28. pthread_sigmask(SIG_SETMASK, &set, NULL);
29.
30. for(;;)
31. {}
32. return 0;
33.}
34.
35.static void
36.sig_alrm(int signo)
37.{
38. printf("after sigwait, catch SIGALRM\n");
39. fflush(stdout);
40. return ;
41.}
42.
43.static void
44.sig_init(int signo)
45.{
46. printf("catch SIGINT\n");
47. return ;
48.}
#include<stdio.h>
#include<pthread.h>
#include<signal.h>
static void sig_alrm(int signo);
static void sig_init(int signo);
int
main()
{
sigset_t set;
int sig;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信號
signal(SIGALRM, sig_alrm);
signal(SIGINT, sig_init);
sigwait(&set, &sig);//sigwait只是從未決隊列中刪除該信號,並不改變信號掩碼。也就是,當sigwait函數返回,它監聽的信號依舊被阻塞。
switch(sig){
case 14:
printf("sigwait, receive signal SIGALRM\n");
/*do the job when catch the sigwait*/
break;
default:
break;
}
sigdelset(&set, SIGALRM);
pthread_sigmask(SIG_SETMASK, &set, NULL);
for(;;)
{}
return 0;
}
static void
sig_alrm(int signo)
{
printf("after sigwait, catch SIGALRM\n");
fflush(stdout);
return ;
}
static void
sig_init(int signo)
{
printf("catch SIGINT\n");
return ;
}

運行命令:

[cpp] view plaincopyprint?

01.第一個終端:

02.huangcheng@ubuntu:~$ ./a.out

03.第二個終端:

04.huangcheng@ubuntu:~$ kill -2 2704

05.huangcheng@ubuntu:~$ kill -2 2704

06.huangcheng@ubuntu:~$ kill -2 2704

07.huangcheng@ubuntu:~$ kill -14 2704

08.huangcheng@ubuntu:~$ kill -14 2704

09.huangcheng@ubuntu:~$ kill -14 2704

10.huangcheng@ubuntu:~$ kill -14 2704

第一個終端:

huangcheng@ubuntu:~$ ./a.out

第二個終端:

huangcheng@ubuntu:~$ kill -2 2704

huangcheng@ubuntu:~$ kill -2 2704

huangcheng@ubuntu:~$ kill -2 2704

huangcheng@ubuntu:~$ kill -14 2704

huangcheng@ubuntu:~$ kill -14 2704

huangcheng@ubuntu:~$ kill -14 2704

huangcheng@ubuntu:~$ kill -14 2704執行結果:

[cpp] view plaincopyprint?

01.huangcheng@ubuntu:~$ ./a.out

02.catch SIGINT

03.catch SIGINT

04.catch SIGINT

05.sigwait, receive signal SIGALRM

06.after sigwait, catch SIGALRM

07.after sigwait, catch SIGALRM

08.after sigwait, catch SIGALRM

huangcheng@ubuntu:~$ ./a.out

catch SIGINT

catch SIGINT

catch SIGINT

sigwait, receive signal SIGALRM

after sigwait, catch SIGALRM

after sigwait, catch SIGALRM

after sigwait, catch SIGALRM

結果說明:如果我們同時注冊了信號處理函數,同時又用sigwait來等待這個信號,誰會取到信號?經過實驗,Ubuntu 10.04上sigwait的優先級高。

Copyright © Linux教程網 All Rights Reserved