作業点滴(五)Linuxマルチスレッドプログラミングにおける信号問題
0はじめに
仕事の秘密保持の原因のため、簡単に背景を紹介します:
本システムにはプロセスAがあり,Aにはそれぞれ5つのスレッドがa,b,c,d,eと生成される.
ここで、
aはメインスレッド(プロセスAとも呼ばれる)であり、epoll(select)を用いてsocketを介して他のプロセスとの通信を担当する.
bはタイマスレッドであり、alarm呼び出しにより信号中断が発生して計時を行う.(主に次のようなコードで行います)
c,d,eは他の作業スレッドである.
1問題発生
初期化時にbスレッド(タイマスレッド)を開くとします.
aスレッドのsocketの通信では「Interrupted system call」(中国語の意味:システム呼び出しが中断される)が発生します.
bスレッドを閉じると、バグは発生しません.
2問題の分析
epollおよびsignalの原理を調べることにより,マルチスレッド呼び出しの信号問題に起因することが分かった.
3問題解決
aスレッドに割り込みシールドを設定することで解決し、他のスレッドがsignalを受け入れることに影響を与えない.
コードは次のとおりです.
4まとめ
4.1 Linuxマルチスレッドアプリケーションでは、各スレッドは、
4.2スレッド呼び出し
4.3信号実装はsigaction方式を採用することが好ましく、sigactionはsignalの代わりに設計された安定した信号処理であり、signalの使用は比較的簡単である.signal(signalNO,signalproc);
完成できない任務は:1.信号が発生した原因を知らない.
2.処理信号で他の信号をブロックすることはできません
signactionでは、より多くのメッセージを設定できます.特に、信号処理関数の過程で信号を受信し、どのような処理を行うか.Sigaction関数は、プロセスが特定の信号を受信した後の動作を変更するために使用される.
4.4 sigprocmask関数は単一スレッドにのみ使用でき、マルチスレッドではpthread_を使用するsigmask関数.
4.5信号はプロセスに送信される特殊なメッセージであり、その典型的な特性は非同期性である.
4.6信号セットは、sigset_のタイプの複数の信号のセットを表すt.
4.7各プロセスには、現在のプロセスがブロックを要求する信号セットを定義する信号マスク(または信号マスクと呼ばれる)がある.
4.8ブロックとは、Linuxカーネルがマスク内のすべての信号をプロセスに渡さないことを意味する.したがって、プロセスは、信号マスクを修正することによって、特定の信号の配信を一時的にブロックすることができ、ブロックされた信号は、信号が本当に配信されるまでプロセスの挙動に影響を及ぼさない.
4.9無視信号はブロック信号とは異なり、無視信号とはLinuxカーネルが生成した信号をアプリケーションに渡したことを意味し、アプリケーションが直接この信号を捨てただけである.
5プログラムインスタンス
実行結果:
仕事の秘密保持の原因のため、簡単に背景を紹介します:
本システムにはプロセスAがあり,Aにはそれぞれ5つのスレッドがa,b,c,d,eと生成される.
ここで、
aはメインスレッド(プロセスAとも呼ばれる)であり、epoll(select)を用いてsocketを介して他のプロセスとの通信を担当する.
bはタイマスレッドであり、alarm呼び出しにより信号中断が発生して計時を行う.(主に次のようなコードで行います)
signal(SIGALRM,timer_handle);
if(setitimer(ITIMER_REAL,&gTimerVal,NULL) == -1)
printf("setitimer error! ");
c,d,eは他の作業スレッドである.
1問題発生
初期化時にbスレッド(タイマスレッド)を開くとします.
aスレッドのsocketの通信では「Interrupted system call」(中国語の意味:システム呼び出しが中断される)が発生します.
bスレッドを閉じると、バグは発生しません.
2問題の分析
epollおよびsignalの原理を調べることにより,マルチスレッド呼び出しの信号問題に起因することが分かった.
3問題解決
aスレッドに割り込みシールドを設定することで解決し、他のスレッドがsignalを受け入れることに影響を与えない.
コードは次のとおりです.
...
struct sigaction action;
bzero(&action,sizeof(action));
action.sa_sigaction = siga_handler;
sigset_t set;
sigemptyset(&set)
sigaddset(&set,SIGALRM);
//sigprocmask(SIG_BLOCK,&set,NULL);
pthread_sigmask(SIG_BLOCK,&set,NULL);
action.sa_mask = set;
action.sa_flags = SA_SIGINFO;
// if(sigaction(SIGINT,&action,NULL)==-1)perror("error"),exit(-1);
if(sigaction(SIGALRM,&action,NULL)==-1)perror("error"),exit(-1);
...
4まとめ
4.1 Linuxマルチスレッドアプリケーションでは、各スレッドは、
pthread_sigmask()
を呼び出して、このスレッドの信号マスクを設定することができる.一般に、ブロックされた信号は、SIGSEGVのようなプログラム実行エラーのために発生しない限り、スレッドの実行を中断することはできない.また処理を無視できない信号SIGKILLやSIGSTOPもブロックできない.4.2スレッド呼び出し
pthread_create()
によって新しいスレッドが作成されると、このスレッドの信号マスクは、新しく作成されたスレッドによって継承される.4.3信号実装はsigaction方式を採用することが好ましく、sigactionはsignalの代わりに設計された安定した信号処理であり、signalの使用は比較的簡単である.signal(signalNO,signalproc);
完成できない任務は:1.信号が発生した原因を知らない.
2.処理信号で他の信号をブロックすることはできません
signactionでは、より多くのメッセージを設定できます.特に、信号処理関数の過程で信号を受信し、どのような処理を行うか.Sigaction関数は、プロセスが特定の信号を受信した後の動作を変更するために使用される.
4.4 sigprocmask関数は単一スレッドにのみ使用でき、マルチスレッドではpthread_を使用するsigmask関数.
4.5信号はプロセスに送信される特殊なメッセージであり、その典型的な特性は非同期性である.
4.6信号セットは、sigset_のタイプの複数の信号のセットを表すt.
4.7各プロセスには、現在のプロセスがブロックを要求する信号セットを定義する信号マスク(または信号マスクと呼ばれる)がある.
4.8ブロックとは、Linuxカーネルがマスク内のすべての信号をプロセスに渡さないことを意味する.したがって、プロセスは、信号マスクを修正することによって、特定の信号の配信を一時的にブロックすることができ、ブロックされた信号は、信号が本当に配信されるまでプロセスの挙動に影響を及ぼさない.
4.9無視信号はブロック信号とは異なり、無視信号とはLinuxカーネルが生成した信号をアプリケーションに渡したことを意味し、アプリケーションが直接この信号を捨てただけである.
5プログラムインスタンス
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
struct sigaction act;
void catchctrlc(int signo)
{
char msg[]="Ctrl-C entered!
";
char errmsg[]="Failed to restore SIGINT
";
int msglen = sizeof(msg);
int emsglen = sizeof(errmsg);
act.sa_handler = SIG_DFL;
write(STDERR_FILENO,msg,msglen);
if(sigaction(SIGINT,&act,NULL) == -1)
write(STDERR_FILENO,errmsg,emsglen);
}
int main()
{
act.sa_handler = catchctrlc;
act.sa_flags = 0;
if((sigemptyset(&act.sa_mask) == -1) ||
(sigaction(SIGINT,&act,NULL) == -1))
perror("Failed to set SIGINT to handle Ctrl-c");
for(;;)
;
return 0;
}
実行結果:
lab@ubuntu:~/ctest/signal$ ./cc //
//
^CCtrl-C entered! // Ctrl+c
//
^C // Ctrl+c
lab@ubuntu:~/ctest/signal$