天天看點

Linux 信号(signal)以及信号的應用

信号概述

1.對于Linux來說,實際信号是軟中斷,許多重要的程式都需要處理信号。信号為Linux提供了一種處理異步事件的方法。比如:終端使用者輸入了ctrl+c來中斷程式,會通過信号機制停止一個程式。

2.信号的名字和編号:

  • 每個信号都有一個名字和編号,這些名字都以"SIG"開頭,例如:“SIGIO”,"SIGCHLD"等等
  • 信号定義在signal.h頭檔案中,信号名都定義為正整數
  • 具體的信号名稱可以用指令 kill -l 來檢視信号的名字以及序号,信号是從1開始編号的,不存在0号信号。kill對于信号0有特殊的應用
    Linux 信号(signal)以及信号的應用

下列是一些信号的具體作用

Linux 信号(signal)以及信号的應用

3.信号的處理:

信号的處理有三種方法,分别是:忽略,捕捉和預設動作。

  • 忽略信号,大多數信号可以使用這個方式來處理,但是有兩種信号不能被忽略(分别是SIGKILL和SIGSTOP)。因為它們向核心和超級使用者提供了程序終止和停止的可靠方法,如果忽略了,那麼這個程序就變成了沒人能管理的程序,顯然是核心設計者不希望看到的場景
  • 捕捉信号,需要告訴核心,使用者希望如何處理某一種信号,說白了就是寫一個信号處理函數,然後将這個函數告訴核心,當該信号産生時,由核心來調用使用者自定義的函數,以此來實作某種信号的處理
  • 系統預設動作,對于每個信号來說,系統都對應由預設的處理動作,當發生了該信号,系統會自動運作。具體的信号預設動作可以使用指令 man 7 signal 來檢視

4.對于常用的kill 指令就是一個發送信号的工具。例如:kill -9 + 程序ID來殺死程序或者kill -SIGKILL + 程序ID 也可以殺死程序

信号程式設計

1.信号處理函數的注冊:signal()函數

*typedef void (sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

參數說明:

  • signum:要捕捉的信号(Linux下檢視信号:kill -l ,9号SIGKILL和19号SIGSTOP信号不能被捕捉)
  • handler:我們要對信号進行的處理方式。信号的處理方式一般有三種:忽略此信号(SIG_IGN),恢複對信号的系統預設處理(SIG_DFL)

demo:

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

void handler(int signum)
{
     printf("get signum = %d\n",signum);
    
     switch(signum){
                  case 2:
                        printf("SIGINT\n");
                        break;

                  case 9:
                        printf("SIGKILL\n");
                        break;

                  case 10:
                        printf("SIGUSR1\n");
                        break;
      }
                  printf("never quit\n");

}

int main()
{
    signal(SIGINT,handler);
    signal(SIGKILL,handler);
    signal(SIGUSR1,handler);

    while(1);
    return 0;

}
           

2.信号處理發送函數:kill()函數

int kill(pid_t pid, int sig);

功能:用于任何程序組或程序發送信号。

傳回值:成功傳回0,失敗傳回-1

參數說明:

  • pid:有四種情況:1)大于0:pid是信号欲送往的程序的辨別;等于0:信号将送往所有與調用kill()的那個程序屬同一個使用組的程序;3)等于-1:信号将送往所有調用程序有權給其發送信号的程序,除了程序1(init程序);4)小于-1:信号将送往以 -pid 為組辨別的程序
  • sig:準備發送的信号代碼,假如其值為0,則沒有任何信号送出,但是系統會執行錯誤檢查,通常會利用sig值為0來檢驗某個程序是否仍在執行

demo:

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
     int signum;
     int pid;

     char cmd[128] = {0};  

     signum = atoi(argv[1]);  // 因為傳進來的參數是字元串,是以要轉化成整形數
     pid = atoi(argv[2]);

     printf("signum=%d,pid=%d\n",signum,pid);

     sprintf(cmd,"kill -%d %d",signum,pid);

     system(cmd);

     printf("send signal ok\n");
  
     return 0;

}
           

信号攜帶消息程式設計

**1.信号注冊函數:sigaction()函數

int sigaction(int signum, const struct sigaction act, struct sigaction oldact);

功能:檢查或修改與指定信号相關聯的處理動作(可同時兩種操作)

傳回值:成功傳回0,失敗傳回-1,錯誤原因置于errno中

參數說明:

  • signum:要捕捉的信号名稱(除了SIGKILL和SIGSTOP)
  • act:指定新的信号處理方式,它是一個結構體
struct sigaction 
{
       void     (*sa_handler)(int);   // 信号處理程式,不接受額外資料
       void     (*sa_sigaction)(int, siginfo_t *, void *);  // 信号處理程式,能接受額外資料,和sigqueue配合使用
       sigset_t   sa_mask;  //  用來設定在處理該信号時暫時将sa_mask指定的信号阻塞
       int        sa_flags;  //  影響信号的行為,SA_SIGINFO表示能夠接受資料
       void     (*sa_restorer)(void);
};


 SA_SIGINFO:
 
 siginfo_t 
{
               int      si_signo;    
               int      si_errno;    
               int      si_code;     
               int      si_trapno;   
               pid_t    si_pid;      
               uid_t    si_uid;      
               int      si_status;   
               clock_t  si_utime;    
               clock_t  si_stime;    
               sigval_t si_value;    
               int      si_int;      
               void    *si_ptr;      
               int      si_overrun;  
               int      si_timerid;  
               void    *si_addr;     
               long     si_band;     
               int      si_fd;       
               short    si_addr_lsb; 
                                        
  }
      
         
           
  • oldact:原來對信号的處理方式。不關心的話寫NULL

2.信号發送函數:sigqueue()函數

int sigqueue(pid_t pid, int sig, const union sigval value);

傳回值:成功傳回0,失敗傳回-1

參數說明:

  • pid:指定接收信号的程序ID
  • sig:确定即将發送的信号
  • 第三個參數是一個聯合體
union sigval
{
      int   sival_int;
      void *sival_ptr;
};

           

說明:使用這兩個函數之前,必須要有幾個操作完成

  • 使用sigaction函數安裝信号處理程式時,制定了SA_SIGINFO的标志
  • sigaction結構體中的sa_sigaction成員提供了信号捕捉函數。如果實作的是sa_handler成員,那麼将無法擷取額外攜帶的資料

// 接收信号端

#include <stdio.h>
#include <signal.h>
#include <string.h>

void handler(int signum, siginfo_t *info, void *context)
{
       printf("get signum %d\n",signum);
       
       if(context != NULL){

                  printf("get data=%d\n",info->si_int);
                    
                  printf("from:%d\n",info->si_pid);
       }

}

int main()
{
    struct sigaction act;
    printf("pid=%d\n",getpid());
  
    act.sa_sigaction = handler;
    act.sa_flags = SA_SIGINFO; 

    sigaction(SIGUSR1,&act,NULL);

    while(1);
    return 0;

}
           

// 發送信号端

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

int main(int argc,char **argv)
{
   int signum;
   int pid;
   
   signum = atoi(argv[1]);
   pid = atoi(argv[2]);

   union sigval value;
   value.sival_int = 100;
 
   sigqueue(pid,signum,value);

   printf("%d, done\n",getpid());

   return 0;

}
           

運作結果:

Linux 信号(signal)以及信号的應用

繼續閱讀