Linux進程間通信-信號


1.什么是信號
信號是Linux系統響應某些條件而產生的一個事件,接收到該信號的進程會執行相應的操作。

2.信號的產生
1)由硬件產生,如從鍵盤輸入Ctrl+C可以終止當前進程
2)由其他進程發送,如可在shell進程下,使用命令 kill -信號標號 PID,向指定進程發送信號。
3)異常,進程異常時會發送信號

3.信號的處理
信號是由操作系統來處理的,說明信號的處理在內核態。
信號不一定會立即被處理,此時會儲存在信號的信號表中。
處理過程示意圖:

由上圖中可看出信號有三種處理方式:
1)忽略
2)默認處理方式:操作系統設定的默認處理方式
3)自定義信號處理方式:可自定義信號處理函數

4.自定義信號處理方式
1)signal函數
原型:
void (*signal(int sig, void (*func)(int)))(int);
sig:信號值
func:信號處理函數指針,參數為信號值
代碼示例如下:

#include <signal.h>
#include <stdio.h>
void ouch(int sig)
{
    printf("\nOUCH! - I got signal %d\n", sig);
    //恢復終端中斷信號SIGINT的默認行為
    (void) signal(SIGINT, SIG_DFL);
}
int main()
{
    //改變終端中斷信號SIGINT的默認行為,使之執行ouch函數
    //而不是終止程序的執行
    (void) signal(SIGINT, ouch);
    while(1)
    {
        printf("Hello World!\n");
        sleep(1);
    }
    return 0;
}

輸出結果:

2)sigaction函數
原型:
int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);
sig:信號值
act:指定信號的動作
oact:保存原信號的動作

sigaction結構體的定義如下:
void (*)(int) sa_handler;處理函數指針,相當於signal函數的func參數。
sigset_t sa_mask;處理過程中,屏蔽對sa_mask信號集的處理,sa_mask可以消除信號間的競態。
int sa_flags;信號處理修改器:處理函數執行完后,信號處理方式修改。如SA_RESETHAND,將信號處理方式重置為SIG_DFL
代碼示例如下:

#include <stdio.h>
#include <signal.h>
void ouch(int sig)
{
    printf("\nOUCH! - I got signal %d\n", sig);
}
int main()
{
    struct sigaction act;
    act.sa_handler = ouch;
    //創建空的信號屏蔽字,即不屏蔽任何信息
    sigemptyset(&act.sa_mask);
    //使sigaction函數重置為默認行為
    act.sa_flags = SA_RESETHAND;
    sigaction(SIGINT, &act, 0);
    while(1)
    {
        printf("Hello World!\n");
        sleep(1);
    }
    return 0;
}

輸出結果:

4.信號的發送
1)kill函數
int kill(pid_t pid,int signo);
pid:進程ID
signo:信號值

2)raise函數:只能向當前進程發信號
int raise(int signo);
signo:信號值

3)abort函數:發送SIGABRT信號,可以讓進程異常終止
void abort(void);

4)alarm函數:發送SIGALRM鬧鍾信號
unsigned int alarm(unsigned int seconds);

5.信號的阻塞
阻塞是阻止進程收到該信號,此時信號處於未決狀態,放入進程的未決信號表中,
當解除對該信號的阻塞時,未決信號會被進程接收。

1)阻塞信號
原型:
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
how:設置block阻塞表的方式
a.SIG_BLOCK:將信號集添加到block表中
b.SIG_UNBLOCK:將信號集從block表中刪除
c.SIG_SETMASK:將信號集設置為block表
set:要設置的集合
oset:設置前保存之前block表信息

2)獲取未決信號
前面已經講過,阻塞的信號處於未決的狀態,會放入進程的未決信號表。
原型:
int sigpending(sigset_t *set);
set:out型參數,會將獲得的當前進程的pending未決表中的信號集傳入。

代碼示例如下:

#include <stdio.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <signal.h>
void func(int num)
{
  printf("catch signal number is %d",num);
}
void printfpendingsignal(sigset_t *set)
{
  int i;
  for(i=1;i<32;++i)
  {
    if(sigismember(set,i))
    {
      printf("1");
    }
    else
    {
      printf("0");
    }
  }
  printf("\n");
}
int main()
{ 
  sigset_t s,p,o;
  signal(SIGINT,func);
  sigemptyset(&s);
  sigemptyset(&p);
  sigemptyset(&o);
  sigaddset(&s,SIGINT);
  sigprocmask(SIG_SETMASK,&s,&o);
  int count=0;
  while(1)
  {
    sigpending(&p);
    printfpendingsignal(&p);
    sleep(1);
    if(count++==10)
    {
      printf("recover!\n");
      sigprocmask(SIG_SETMASK,&o,NULL);
    }
  }
  return 0;
}

輸出結果:

6.信號處理函數的安全問題
如果信號處理過程中被中斷,再次調用,然后返回到第一次調用時,要保證操作的正確性。
這就要求信號處理函數必須是可重入的。
可重入函數表如下:

7.一些常見的信號

如果進程接收到上面的這些信號,又沒有安排捕獲它,進程就會終止。

其他的一些信號如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM