linux 0.11バージョンカーネルにおける信号signal()とsigaction()の違い


この2つの信号プログラムに関する理解を記録します.signal()とsigaction()は主にある信号に対するプロセッサを設定するために使用されます.この2つのシステム呼び出しは、まずプロセス独自のタスクデータ構造にsigaction[]構造配列を設定します.この要素構造は次の図に示します.
struct sigaction {
	void (*sa_handler)(int);  //       
	sigset_t sa_mask;       //     ,             
	int sa_flags;           //    
	void (*sa_restorer)(void);//        ,        do_signal       
};
配列の各々は、信号に関する設定を表す.カーネルがシステム呼び出しを終了し、一部の割り込み中に現在のプロセスが信号を受信したかどうかを検出し、ユーザーが指定した信号を受信すると、カーネルはプロセス中のsigaction[]の対応する信号の構造項目に基づいてユーザー定義の信号処理プログラムを実行します.次にsignal()のソースコードを見てみましょう.
int sys_signal(int signum, long handler, long restorer)
{
	struct sigaction tmp;

	if (signum<1 || signum>32 || signum==SIGKILL)
		return -1;
	tmp.sa_handler = (void (*)(int)) handler;
	tmp.sa_mask = 0;
	tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
	tmp.sa_restorer = (void (*)(void)) restorer;
	handler = (long) current->sigaction[signum-1].sa_handler;
	current->sigaction[signum-1] = tmp;
	return handler;
}

解析:信号セットは32ビットのビットマップであるため、まず信号が範囲内ではなく、SIGKILL信号ではないことを確認します.次に、1つのtmpを利用して、その処理関数を入力された処理関数に設定し、信号シールドコードは0、すなわち信号を遮断せず、信号オプションフラグビットは1回使用するとデフォルト値に戻るように設定し、信号処理中にこの信号も受信でき、その後、tmpを利用してcurrent->sigaction[signum-1]を設定すればよい.さらにsigaction()を見てみると、ソースコードは以下の通りです.
int sys_sigaction(int signum, const struct sigaction * action,
	struct sigaction * oldaction)
{
	struct sigaction tmp;

	if (signum<1 || signum>32 || signum==SIGKILL)
		return -1;
	tmp = current->sigaction[signum-1];
	get_new((char *) action,
		(char *) (signum-1+current->sigaction));
	if (oldaction)
		save_old((char *) &tmp,(char *) oldaction);
	if (current->sigaction[signum-1].sa_flags & SA_NOMASK)
		current->sigaction[signum-1].sa_mask = 0;
	else
		current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
	return 0;
}
分析:まずcurrent->sigaction[signum-1]をtmpに保存し、sigaction[]に新しい操作を設定します.渡されたoldactionが空でない場合は、このメモリを使用してtmpを一時保存し、flagに基づいてハンドルで本信号を遮断するかどうかを決定します.SA_が設定されている場合は、NOMASKは、マスクする必要はありません.そうしないと、本信号をマスクします.最後のシールドの意味を理解すると、このシールドが設定されている場合、信号処理中に同じ信号がビットマップにセットされても、すぐに信号処理関数を呼び出すのではなく、現在の処理関数の終了を待つ.また、複数の信号が来ても、ビットマップが1つしかなく、1つの信号しか保存できず、その他は失われます.
実は彼らの違いは分かりやすいのですが、signal()関数は呼び出すたびにデフォルトの処理関数に戻り、ユーザーが設定した処理関数を引き続き使用するには信号処理プログラムでもう一度signal()関数を呼び出す必要がありますが、次回の設定前に来た信号はデフォルトの操作を使用して失われます.一方,sigaction()設定後はユーザ設定の処理関数を常に保持できる.