信号 (研究ノート)

1. シグナルとは

1.1. シグナルの概要

     シグナルは、特定の条件に応答して UNIX および Linux システムによって生成されるイベントであり、シグナルを受信したプロセスはそれに応じて何らかのアクションを実行します。この信号は、ソフトウェア レベルでの割り込みメカニズムのシミュレーションであるソフト割り込みです。.

シグナルは、予期しないイベントを処理するために、実行中の別の非同期プロセスによって実行中のプロセスが中断される可能性があります。

1.2. シグナルは非同期通信方式

        プロセスはシグナルが到着するのを待つ必要がなく、プロセスはシグナルがいつ到着するかを知りません。
この信号は、ユーザー空間プロセスとカーネル空間プロセスの間で直接やり取りでき、カーネル プロセスはそれを使用して、どのシステム イベントが発生したかをユーザー空間プロセスに通知できます。

1.3. 信号特性

各シグナルの名前は、文字 SIG で始まります。
各信号はデジタル コードに対応し、ヘッダー ファイル signum.h では、これらの信号は正の整数として定義されています。

シグナル名定義パス:
             /usr/include/i386-linux-gnu/bits/signum.h
Linux では、これらのシグナルとコードの対応を表示するには、次のコマンドを使用できます: kill -l

 1.4. ショートカットキーが信号を生成する

1. ユーザーが特定の端末キーを押すと、信号が生成されます。
例:
端末で「Ctrl+c」キーの組み合わせを押すと、通常、割り込みシグナル SIGINT が生成され、端末で「Ctrl+\」キーを押すと、通常、割り込みシグナル SIGQUIT が生成され、「Ctrl+z」キーを押すと、通常は割り込みシグナルが生成されます。通常、端末は割り込みシグナル SIGSTOP を生成します。
2. ハードウェア例外によりシグナルが生成されます。
0 による除数、無効なメモリ アクセスなど。これらの状態は通常、ハードウェアによって検出され、カーネルに通知されます。カーネルは、対応するプロセスに適切なシグナルを生成します。
3. ソフトウェア異常により信号が発生します。
ソフトウェア条件の発生が検出されると、シグナルが生成され、プロセスに通知されます。(たとえば、子プロセスが一時停止または終了すると、親子プロセスは自動的に SIGCHLD シグナルを送信します)

二、合図信号機能

ヘッドファイル:

                 #include <signal.h>

関数:

                typedef void (*sighandler_t)(int);
                sighandler_t signal(int signum,sighandler_t handler);
機能:
                 シグナル処理関数の登録、つまりシグナル受信後の処理関数のエントリアドレスの決定。
パラメータ:
                signum: シグナル番号
                handler 値:
                              シグナルを無視: SIG_IGN は
                              システムのデフォルト アクションを実行します: SIG_DFL
                               カスタム シグナル処理関数:シグナル処理関数名
戻り値:
               success: このシグナルに対して最後に登録された関数アドレスを返します。シグナルハンドラ関数。
                 失敗: SIG_ERR を返す
 

注: SIGKILL と SIGSTOP の 2 つのシグナルを無視したりキャプチャしたりすることはできません。

2.1. 集計機能

                      sighandler_t signal( int signum,   sighandler_t ハンドラ);

この関数を見つけるのは難しくありません。最初のパラメーターは kill -l の下のシグナルの 1 つに設定されます ( SIGKILL、SIGSTOP を除く)。

2 番目のパラメーターには、次の 3 つのオプションがあります。

1 つ目は SIG_IGN に設定されます。  最初のパラメーター信号は無視され
、2 番目の設定はSIG_DFL です。  1番目のパラメータのアクションを実行し                            
、3番目の設定は信号処理関数名 、カスタム信号処理です

3. シグナル機能のユースケース

3.1、信号は無視モードに設定されています

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
	
	if(signal(SIGINT,SIG_IGN)==SIG_ERR)
  {
	  
	   perror("fail to signal : sigint");
	   exit(1);
	    
	    }
	
	if(signal(SIGQUIT,SIG_IGN)==SIG_ERR)
	{
		
		perror("fail to signnal : singquit");
		exit(1);
		}
    	
	  while(1)
	  {
		  printf("Hello world\n");
		  sleep(1);
		    
			}
	
	 return 0;
	
	 }

このコードは、「Ctrl+\」キーまたは「Ctrl+c」を押して端末にシグナルを送信するように設定されています。これは無視されます。つまり、一時停止または終了しません。

運用実績

 

3.2、信号はデフォルトモードとして設定されています

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
	
	if(signal(SIGINT,SIG_DFL)==SIG_ERR)
  {
	  
	   perror("fail to signal : sigint");
	   exit(1);
	    
	    }

	
	
	if(signal(SIGQUIT,SIG_DFL)==SIG_ERR)
	{
			
		perror("fail to signnal : singquit");
		exit(1);
		
		}
    	
	  while(1)
	  {
		  printf("Hello world\n");
		  sleep(1);
		    
			}
	
	
	
	 return 0;
	
	 }

このコードは、「Ctrl+\」キーまたは「Ctrl+c」を押して端末にシグナルを送信するように設定されており、デフォルトで実行が終了または終了します。

運用実績

3.3、信号はカスタム信号処理に設定されています

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>

void hander(int sig);


int main()
{
	
	if(signal(SIGINT,hander)==SIG_ERR)
  {
	  
	   perror("fail to signal : sigint");
	   exit(1);
	    
	    }

	
	
	if(signal(SIGQUIT,hander)==SIG_ERR)
	{
			
		perror("fail to signnal : singquit");
		exit(1);
		
		}
    	
	  while(1)
	  {
		  printf("Hello world\n");
		  sleep(1);
		    
			}

   
       return 0;
}

	void hander(int sig)
 {
       if(sig == SIGINT)
	 {
	    printf("SIGINT正在处理\n");
	  }
		
	    if(sig == SIGQUIT)
	 {
		 printf("SIGQUIT正在处理\n");
	
	  }
	
	 }

このコードはシグナルのみをキャプチャし、キャプチャしたシグナルを介して 2 番目のパラメーターの内容を実行しますが、終了または終了しません。

運用実績

 

4. 拡張: 親子プロセス、シグナルシグナル関数を使用して子プロセスのリソースを再利用します (強調)

 4.1. SIGCHLD の概念

        プロセスが正常終了しても異常終了しても、システム カーネルによって SIGCHLD (17) シグナルがその親プロセスに送信されます。親プロセスは、簡潔で効率的な SIGCHLD (17) signal のシグナル処理関数で、子プロセス (つまり、ゾンビ プロセス)のリソースを非同期的に再利用できます。

4.2、ゾンビプロセスとは

子プロセスはそれ自体で終了し、親プロセスは子プロセスの状態をクリーンアップするために wait または waitpid 関数を呼び出さなかったので、この状態は常にプロセス リストに存在します。

4.3. ゾンビプロセスを解決する 2 つの一般的な方法

    1. 親プロセスは、wait/waitpid 関数を呼び出して、子プロセスのリソースを再利用する必要があります。

    2. signal シグナル関数を使用して、子プロセスのリソースを再利用します。

5. ケース: signal シグナル関数を使用して、子プロセス (つまり、ゾンビ プロセス) をリサイクルします。

5.1. ゾンビプロセスの例 (signal シグナル関数を使用しない場合)

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
	pid_t pid;

    pid= fork();
  if(pid < 0)
  {
	  perror("create fork fail ");
	  exit(1);
	  } 
	   
      if(pid==0)
	 {
		 while(1)
		{
		   static int i=0;
			 i++;
		 printf("this is son : pid =%d\n",getpid());	 
		     sleep(1);
			 if(i==3)
			 {
				 exit(1);
				 }
			 }
		 
		 }
		 
	 else if(pid>0)
	 {
		 while(1)
		 {
	      printf("Hello this is father: pid =%d\n",getpid());
		  sleep(1);
		 }
	 }	  
	    		   
       return 0;
}

実行結果(下図の赤枠Z+がゾンビプロセス

 5.2. signal シグナル関数を使用してゾンビプロセスをリサイクルする

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
void handler(int sig)
{
	wait(NULL);
	
	}
 
int main()
{
	pid_t pid;

    pid= fork();

	signal(SIGCHLD,handler);

  if(pid < 0)
  {
	  perror("create fork fail ");
	  exit(1);
	  } 
	   
      if(pid==0)
	 {
		 while(1)
		{
		   static int i=0;
			 i++;
		 printf("this is son : pid =%d\n",getpid());	 
		     sleep(1);
			 if(i==3)
			 {
				 exit(1);
				 }
		     }		 
		 }
		 
	 else if(pid>0)
	 {
		 while(1)
		 {
	      printf("Hello this is father: pid =%d\n",getpid());

		  sleep(1);
		 }
	 }	  
	   	   
       return 0;
}


操作の結果 (赤いボックスが通常の S+ に変わります) は、リサイクルが成功したことを意味します。

 

 

おすすめ

転載: blog.csdn.net/weixin_47783699/article/details/128002312