linux 進程通訊之 信號

一,管道PIPE

二,FIFO通訊

三,mmap通訊

四,信號的概念

信號的特色:簡單,但不能攜帶大量的信息,知足特定條件就會發生

信號的機制:進程B發送信號給進程A。信號是由內核來處理的。

信號的產生:

  • 按鍵產生:ctrl+c,ctrl+z,ctrl+ - Ctrl + c → 2) SIGINT(終止/中斷) "INT" ----Interrupt
    • Ctrl + z → 20) SIGTSTP(暫停/中止) "T" ----Terminal 終端。
    • Ctrl +   → 3) SIGQUIT(退出)
  • 調用系統函數後產生:kill,raise,abort等
  • 定時器產生:alarm,setitimer
  • 命令產生:kill等
  • 因爲硬件錯誤產生:非法訪問內存(段錯誤),除0(浮點數錯誤),內存對齊錯誤(總線錯誤),管道破裂(fd已經關閉了還調用write函數時)。
    • 除0操做 → 8) SIGFPE (浮點數例外) "F" -----float 浮點數。
    • 非法訪問內存 → 11) SIGSEGV (段錯誤)
    • 總線錯誤 →7) SIGBUS
    • 管道破 → 13)SIGPIPE

信號的狀態:

  • 產生
  • 遞達:信號到達了,而且處理完了
  • 未決:產生和遞達之間的狀態。主要因爲阻塞(屏蔽)致使該狀態。

信號的默認處理方式:

  • 忽略
  • 執行默認動做
  • 捕獲

信號的4要素:

  • 編號html

  • 事件linux

  • 名稱c++

  • 默認處理動做程序員

    • 忽略算法

    • 終止shell

    • 終止併產生core文件微信

    • 暫停網絡

    • 繼續併發

    • 下面是英語:異步

      Term   Default action is to terminate the process.
         Ign    Default action is to ignore the signal.
         Core   Default action is to terminate the process and  dump  core  (see
                core(5)).
         Stop   Default action is to stop the process.
         Cont   Default  action  is  to  continue the process if it is currently
                stopped.

用man 7 signal能夠查看詳細的信息

下面是從man 7 signal裏考出來的linux常規信號一覽表,value裏有3列的,看中間的數字,中間的數字表明linux

Signal     Value     Action   Comment
       ──────────────────────────────────────────────────────────────────────
       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard
       SIGQUIT       3       Core    Quit from keyboard
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating-point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers; see pipe(7)
       SIGALRM      14       Term    Timer signal from alarm(2)
       SIGTERM      15       Term    Termination signal
       SIGUSR1   30,10,16    Term    User-defined signal 1
       SIGUSR2   31,12,17    Term    User-defined signal 2
       SIGCHLD   20,17,18    Ign     Child stopped or terminated
       SIGCONT   19,18,25    Cont    Continue if stopped
       SIGSTOP   17,19,23    Stop    Stop process
       SIGTSTP   18,20,24    Stop    Stop typed at terminal
       SIGTTIN   21,21,26    Stop    Terminal input for background process
       SIGTTOU   22,22,27    Stop    Terminal output for background process

中文解釋:

1) SIGHUP: 當用戶退出shell時,由該shell啓動的全部進程將收到這個信號,默認動做爲終止進程
2) SIGINT:當用戶按下了<Ctrl+C>組合鍵時,用戶終端向正在運行中的由該終端啓動的程序發出此信號。默認動
做爲終止進程。
3) SIGQUIT:當用戶按下<ctrl+\>組合鍵時產生該信號,用戶終端向正在運行中的由該終端啓動的程序發出些信
號。默認動做爲終止進程。
4) SIGILL:CPU檢測到某進程執行了非法指令。默認動做爲終止進程併產生core文件
5) SIGTRAP:該信號由斷點指令或其餘 trap指令產生。默認動做爲終止里程 併產生core文件。
6) SIGABRT: 調用abort函數時產生該信號。默認動做爲終止進程併產生core文件。
7) SIGBUS:非法訪問內存地址,包括內存對齊出錯,默認動做爲終止進程併產生core文件。
8) SIGFPE:在發生致命的運算錯誤時發出。不只包括浮點運算錯誤,還包括溢出及除數爲0等全部的算法錯誤。默認動做爲終止進程併產生core文件。
9) SIGKILL:無條件終止進程。本信號不能被忽略,處理和阻塞。默認動做爲終止進程。它向系統管理員提供了能夠殺死任何進程的方法。
10) SIGUSE1:用戶定義 的信號。即程序員能夠在程序中定義並使用該信號。默認動做爲終止進程。
11) SIGSEGV:指示進程進行了無效內存訪問。默認動做爲終止進程併產生core文件。
12) SIGUSR2:另一個用戶自定義信號,程序員能夠在程序中定義並使用該信號。默認動做爲終止進程。
13) SIGPIPE:Broken pipe向一個沒有讀端的管道寫數據。默認動做爲終止進程。
14) SIGALRM: 定時器超時,超時的時間 由系統調用alarm設置。默認動做爲終止進程。
15) SIGTERM:程序結束信號,與SIGKILL不一樣的是,該信號能夠被阻塞和終止。一般用來要示程序正常退出。執行shell命令Kill時,缺省產生這個信號。默認動做爲終止進程。
16) SIGSTKFLT:Linux早期版本出現的信號,現仍保留向後兼容。默認動做爲終止進程。
17) SIGCHLD:子進程結束時,父進程會收到這個信號。默認動做爲忽略這個信號。
18) SIGCONT:若是進程已中止,則使其繼續運行。默認動做爲繼續/忽略。
19) SIGSTOP:中止進程的執行。信號不能被忽略,處理和阻塞。默認動做爲暫停進程。
20) SIGTSTP:中止終端交互進程的運行。按下<ctrl+z>組合鍵時發出這個信號。默認動做爲暫停進程。
21) SIGTTIN:後臺進程讀終端控制檯。默認動做爲暫停進程。
22) SIGTTOU: 該信號相似於SIGTTIN,在後臺進程要向終端輸出數據時發生。默認動做爲暫停進程。
23) SIGURG:套接字上有緊急數據時,向當前正在運行的進程發出些信號,報告有緊急數據到達。如網絡帶外數據到達,默認動做爲忽略該信號。
24) SIGXCPU:進程執行時間超過了分配給該進程的CPU時間 ,系統產生該信號併發送給該進程。默認動做爲終止進程。
25) SIGXFSZ:超過文件的最大長度設置。默認動做爲終止進程。
26) SIGVTALRM:虛擬時鐘超時時產生該信號。相似於SIGALRM,可是該信號只計算該進程佔用CPU的使用時間。默認動做爲終止進程。
27) SGIPROF:相似於SIGVTALRM,它不公包括該進程佔用CPU時間還包括執行系統調用時間。默認動做爲終止進程。
28) SIGWINCH:窗口變化大小時發出。默認動做爲忽略該信號。
29) SIGIO:此信號向進程指示發出了一個異步IO事件。默認動做爲忽略。
30) SIGPWR:關機。默認動做爲終止進程。
31) SIGSYS:無效的系統調用。默認動做爲終止進程併產生core文件。
34) SIGRTMIN ~ (64) SIGRTMAX:LINUX的實時信號,它們沒有固定的含義(能夠由用戶自定義)。全部的實時信號的默認動做都爲終止進程。

信號的編號

可使用kill –l命令查看當前系統可以使用的信號有哪些。

信號集合

  • 阻塞信號集(信號屏蔽字): 將某些信號加入集合,對他們設置屏蔽,當屏蔽x信號後,再收到該信號,該信號的處理將推後(解除屏蔽後)
  • 未決信號集:
    • 信號產生,未決信號集中描述該信號的位馬上翻轉爲1,表信號處於未決狀態。當信號被處理對應位翻轉回爲0。這一時刻每每很是短暫。
    • 信號產生後因爲某些緣由(主要是阻塞)不能抵達。這類信號的集合稱之爲未決信號集。在屏蔽解除前,信號一直處於未決狀態。

系統函數產生的信號

  • kill函數

    #include <sys/types.h>
    #include <signal.h>
    
    int kill(pid_t pid, int sig);
    • sig:信號。不推薦直接使用數字,應使用宏名,由於不一樣操做系統信號編號可能不一樣,但名稱一致。
    • pid > 0: 發送信號給指定的進程。
    • pid = 0: 發送信號給 與調用kill函數進程屬於同一進程組的全部進程。
    • pid < 0: 取|pid|發給對應進程組。
    • pid = -1:發送給進程有權限發送的系統中全部進程。
    • 返回值:成功(0),失敗(-1)。

    例子:在子進程中調用kill函數,殺死父進程

    #include <sys/types.h>
    #include <signal.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main(){
      int proIdx = 0;
      for(proIdx = 0; proIdx < 5; ++proIdx){
        pid_t pid = fork();
        if(pid == 0){
          break;
        }
      }
    
      //在第三個子進程中殺死父進程
      if(proIdx == 2){
        printf("kill parent process after 5s\n");
        sleep(5);
        int ret = kill(getppid(), SIGKILL);
        while(1){
          sleep(1);
        }
      }
      //parent process
      if(proIdx == 5){
        while(1){
          printf("father\n");
          sleep(1);
        }
      }
    }

    例子:在父進程中調用kill函數,殺死子進程

    #include <sys/types.h>
    #include <signal.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main(){
      pid_t pid3, pid;
    
      int proIdx = 0;
      for(proIdx = 0; proIdx < 5; ++proIdx){
        pid = fork();
        if(pid == 0){
          break;
        }
        //parent process
        if(proIdx == 2){
          pid3 = pid;
        }
      }
    
      if(proIdx < 5){
    
        while(1){
          printf("pid=%d\n", getpid());
          sleep(3);
        }
      }
      //在父進程中殺死第三個子進程 
      if(proIdx == 5){
        sleep(5);
        int ret = kill(pid3, SIGKILL);
        while(1){
          sleep(1);
        }
      }
    }
  • raise函數:給調用raise函數的進程發送信號,實際調用的是kill(getpid(), sig)函數

    #include <signal.h>
    
    int raise(int sig);
    • sig:信號。不推薦直接使用數字,應使用宏名,由於不一樣操做系統信號編號可能不一樣,但名稱一致。

    例子:

    #include <signal.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int main(){
      printf("will kill myself\n");
      sleep(2);
      raise(SIGKILL);
      return 0;
    }
  • abort函數:給調用abort函數的進程發送信號,並生成core文件(生成core文件的前提是:ulimit -c unlimited)

    #include <stdlib.h>
    
    void abort(void);
    #include <signal.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main(){
      printf("will kill myself\n");
      sleep(2);
      abort();
      return 0;
    }
  • alarm函數:在指定參數的秒數後,給調用alarm函數的進程發送SIGALRM信號。SIGALRM信號的默認處理的Term,因此就會終止當前的進程。

    Signal     Value     Action
    SIGALRM      14       Term
    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);
    • seconds:指定秒數後發送SIGALRM信號
    • 返回值:第一次返回0。返回第一次和第二次之間的秒數。(returns the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously scheduled alarm.)

    例子:

    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
      int ret = 0;
      ret = alarm(2);
      printf("ret:%d\n", ret);
      sleep(1);
      ret = alarm(5);
      printf("ret:%d\n", ret);
      while(1){
        printf("aaaaaaaaaa\n");
        sleep(1);
      }
    }
  • setitimer函數:功能強大的alarm函數,能夠設置週期性的鬧鐘。

    #include <sys/time.h>
    int getitimer(int which, struct itimerval *curr_value);
    int setitimer(int which, const struct itimerval *new_value,
                         struct itimerval *old_value);
    • which:

      • ITIMER_REAL:按天然時鐘的方式計時。鬧鐘響了後,產生SIGALRM信號。
      • ITIMER_VIRTUAL:按進程執行時間的方式計時。鬧鐘響了後,產生SIGVTALRM信號。
      • ITIMER_PROF:按進程執行時間的方式和CPU時鐘的方式計時。鬧鐘響了後,產生SIGPROF信號。
      Signal     Value     Action   Comment
      SIGALRM      14       Term    Timer signal from alarm(2)
      SIGVTALRM   26,26,28  Term    Virtual alarm clock (4.2BSD)
      SIGPROF     27,27,29  Term    Profiling timer expired

      能夠看出來,這3個信號的默認處理都是Term,因此就會終止當前的進程。

    • new_value和old_value的結構體

      struct itimerval {
        struct timeval it_interval; /* Interval for periodic timer */ 週期定時用
        struct timeval it_value;    /* Time until next expiration */ 單次定時用
      };
      
      struct timeval {
        time_t      tv_sec;         /* seconds */ 秒
        suseconds_t tv_usec;        /* microseconds */ 微妙
      };
    • 返回值:成功0,失敗-1。

    例子:週期性的鬧鐘,第一次鬧鐘在5秒後,而後每3秒鬧鐘響一次。鬧鐘響後調用函數cap,並把信號SIGALRM傳給函數。

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <signal.h>
    
    void cap(int num){
      printf("cap:%d\n", num);
    }
    int main(){
      signal(SIGALRM, cap);//當本進程收到SIGALRM信號時,調用函數cap,並把信號SIGALRM對應的數字傳給cap
      struct itimerval it = {{3,0}, {5,0}};//第一次鬧鐘在5秒後,而後每3秒鬧鐘響一次。
      setitimer(ITIMER_REAL, &it, NULL);
      while(1){
        printf("aaaaa\n");
        sleep(1);
      }
    }

信號集函數

1,操做信號集合

#include <signal.h>
//把對應的標識位所有設置成0
int sigemptyset(sigset_t *set);
//把對應的標識位所有設置成1
int sigfillset(sigset_t *set);
//添加某個信號到信號集
int sigaddset(sigset_t *set, int signum);
//從信號集刪除某個信號……
int sigdelset(sigset_t *set, int signum);
//判斷某個信號是否在信號集裏
int sigismember(const sigset_t *set, int signum);

2,設置阻塞信號

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how
    • SIG_BLOCK:設置想要阻塞的信號
    • SIG_UNBLOCK:設置想要解除阻塞的信號
    • SIG_SETMASK:讓新的set替換掉原來的信號集合

3,取得當前未決信號集

#include <signal.h>
int sigpending(sigset_t *set);
  • set:未決的信號集

4,例子:設置ctrl+c的信號爲阻塞。

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

int main(){

  sigset_t setu;
  //清空信號集合setu
  sigemptyset(&setu);
  //把信號SIGINT加入信號集合setu
  sigaddset(&setu, SIGINT);
  //把信號SIGQUIT加入信號集合setu
  sigaddset(&setu, SIGQUIT);

  //把集合裏面的信號設置爲阻塞。
  sigprocmask(SIG_BLOCK, &setu, NULL);
  
  while(1){
    sigset_t set1;
    sigpending(&set1);
    for(int i = 1; i < 31; ++i){
      //判斷信號i是否在信號集合set1裏面。
      if(sigismember(&set1, i) == 1){
        printf("1");
      }
      else {
        printf("0");
      }
    }
    printf("\n");
    sleep(1);
  }
}

信號捕捉

1,信號捕捉函數signal

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • signum:要捕捉的信號
  • handler:捕捉到信號後,調用的函數指針。返回值爲void,參數爲int(信號的值)

2,信號捕捉函數sigaction

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
  • signum:要捕捉的信號

  • act:捕捉到信號後,執行的動做定義在結構體裏。

    struct sigaction {
      void     (*sa_handler)(int);//當sa_flags爲0時,調用的函數指針
      void     (*sa_sigaction)(int, siginfo_t *, void *);//當sa_flags爲SA_SIGINFO時,調用的函數指針,這個函數能夠傳進去不少參數,可是信號自己的目的就是簡單並攜帶少許信息,因此通常不使用這個參數
      sigset_t   sa_mask;//在執行函數期間,指定的臨時的阻塞信號集合。
      int        sa_flags;//通常爲0,根據這個flag來決定調用哪一個函數。SA_RESTART的做用是,好比某些阻塞函數(read)在阻塞的時候,忽然來了個信號,執行完信號捕捉函數後,會繼續執行阻塞的函數(read),不設置sa_flags爲SA_RESTART的話,信號捕捉函數信息後,不繼續執行阻塞的函數(read)。
      void     (*sa_restorer)(void);
    };
  • oldact:原來對應這個信號的動做。目的是,方便還原回去。

例子1:

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>

void catch(int num){
  printf("catch %d signal\n", num);
}
int main()
{
  struct sigaction act;
    
  act.sa_flags = 0;
  act.sa_handler = catch;
  sigemptyset(&act.sa_mask);

  sigaction(SIGALRM, &act, NULL);

  struct itimerval it = {{3, 0}, {5, 0}};
  setitimer(ITIMER_REAL, &it, NULL);
  while(1){
    printf("kill me plz\n");
    sleep(1);
  }
}

4,信號捕捉函數執行期間的特性

  • X信號捕捉函數的執行期間,X信號被自動屏蔽
  • 阻塞的常規信號不支持排隊,即便產生屢次,函數只被調用一次。(後32個實時信號支持排隊)
  • 進程運行時,有一個屏蔽的信號集合,假定爲集合A。對於函數sigaction來講,在信號捕捉函數執行期間,屏蔽的信號集合不是集合A,而是集合sa_mask。

驗證的例子:

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>

void catch(int num){
  printf("begin catch %d signal\n", num);
  sleep(5);
  printf("end catch %d signal\n", num);
}

int main(){

  struct sigaction act;
  act.sa_flags = 0;
  act.sa_handler = catch;
  sigemptyset(&act.sa_mask);
  sigaddset(&act.sa_mask, SIGQUIT);

  sigaction(SIGINT, &act, NULL);
  
  while(1){
    printf("kill me\n");
    sleep(1);
  }
}
  • 在函數catch執行期間,即使屢次按ctrl c,最後也只執行一次。
  • 在函數catch執行期間,即便按ctrl ,也沒有效果,由於ctrl \產生的信號(SIGQUIT)被屏蔽了。
  • 不在函數catch執行期間,按ctrl \後,進程就當即結束了。由於信號(SIGQUIT)沒有被屏蔽。

用信號機制回收子進程。

原理:當子進程被暫停或者終止的時候,會給父進程發送信號SIGCHLD,信號SIGCHLD的默認處理爲Ign,也就是什麼都不作。因此咱們能夠經過捕捉信號SIGCHLD,在信號捕捉函數裏調用wait或者waitpid函數來回收子進程。

Signal     Value     Action   Comment
 ───────────────────────────────────────────────────────────
SIGCHLD   20,17,18    Ign     Child stopped or terminated

1,錯誤的粗略版,不可以回收所有的子進程。理由是,信號SIGCHLD是不支持排隊的,因此當waitpid函數執行的同時,又有一個子進程結束了,也就是又來了一個SIGCHLD信號,因此這個信號就被忽略了。

重點看最後一行的sleep函數。若是把最後一行的sleep函數的註釋打開,就可以所有回收子進程了。理由是睡了1秒,在waitpid函數執行的同時,沒有別的SIGCHLD信號進來,因此就都回收了。

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

void catch(int num){
  pid_t pid = waitpid(-1, NULL, WNOHANG);
  printf("%d is ok\n", pid);
}

int main(){

  int i = 0;
  for(; i < 10; ++i){
    pid_t p = fork();
    if(p == 0)break;
  }

  if(i == 10){
    //parent process
    struct sigaction act, old;
    act.sa_flags = 0;
    act.sa_handler = catch;
    sigemptyset(&act.sa_mask);

    sigaction(SIGCHLD, &act, NULL);
    while(1){
      sleep(1);
    }
  }
  else {
    //child process
    printf("child %d %d\n", i, getpid());
    //sleep(1);
  }
}

2,改進版,即便註釋掉最後一行的sleep函數,也可以回收所有的子進程。理由是虛幻調用waitpid函數了。在一次信號捕捉函數裏,儘可能多的回收子進程,可是也不必定是絕對的穩定,極端時候也會有回收不全的狀況。每次執行的時候就會發現,打印的【begin】和【end】不必定是幾回。也就是說在一次捕捉函數裏,調用了屢次waitpid函數。並且還有一個致命的弱點,若是父進程的信號捕捉函數sigaction尚未調用,子進程就所有結束了的話,因此的子進程都不可以被回收了。

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

void catch(int num){
  printf("begin\n");
  pid_t pid;
  while((pid = waitpid(-1, NULL, WNOHANG)) > 0){
    printf("%d is ok\n", pid);
  }
  printf("end\n");
}

int main(){

  int i = 0;
  for(; i < 10; ++i){
    pid_t p = fork();
    if(p == 0)break;
  }

  if(i == 10){
    //parent process
    struct sigaction act, old;
    act.sa_flags = 0;
    act.sa_handler = catch;
    sigemptyset(&act.sa_mask);

    sigaction(SIGCHLD, &act, NULL);
    while(1){
      sleep(1);
    }
  }
  else {
    //child process
    printf("child %d %d\n", i, getpid());
  }
}

3,最終版。解決改進版的問題。

原理:在父進程裏阻塞信號SIGCHLD,這樣一來,即便子進程在父進程調用sigaction以前就結束了,子進程發送過來的信號SIGCHLD是被阻塞的狀態,也就是信號處於未決狀態。在調用sigaction後的下一行代碼,把信號SIGCHLD從阻塞狀態恢復到原來的非阻塞狀態後,這個時點全部被阻塞的子進程的信號SIGCHLD,就一次(【begin】和【end】只被打印一次)都被捕捉函數處理掉了。

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

void catch(int num){

  printf("begin\n");
  pid_t pid;
  while((pid = waitpid(-1, NULL, WNOHANG)) > 0){
    printf("%d is ok\n", pid);
  }
  printf("end\n");
}

int main(){

  sigset_t st, oldset;
  sigemptyset(&st);
  sigaddset(&st, SIGCHLD);
  //把信號SIGCHLD設置爲阻塞,並取得當前的信號集合oldset
  sigprocmask(SIG_BLOCK, &st, &oldset);

  int i = 0;
  for(; i < 10; ++i){
    pid_t p = fork();
    if(p == 0)break;
  }

  if(i == 10){
    //parent process
    sleep(2);
    struct sigaction act, old;
    act.sa_flags = 0;
    act.sa_handler = catch;
    sigemptyset(&act.sa_mask);

    sigaction(SIGCHLD, &act, NULL);
    //用信號集合oldset,恢復到以前的狀態。
    sigprocmask(SIG_SETMASK, &oldset, NULL); 

    while(1){
      sleep(1);
    }
  }
  else {
    //child process
    printf("child %d %d\n", i, getpid());
    //sleep(1);
  }
}

父子進程經過信號SIGUSR1和SIGUSR2來通訊,可是執行不成功。執行結果是子進程直接結束了,變成了殭屍進程,求指點!!!

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

void fcat(int num){
  sleep(1);
  printf("aa");
}
void fcat2(int num){
  sleep(1);
  printf("aa1");
}
int main(){
  pid_t pid = fork();
  if(pid == 0){
    
    sigset_t st, oldset;
    sigemptyset(&st);
    sigaddset(&st, SIGUSR2);
    if((sigprocmask(SIG_BLOCK, &st, &oldset)) == -1){
      perror("sigprocmask");
    }

    
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = fcat2;

    sigaction(SIGUSR2, &act, NULL);
    sigprocmask(SIG_SETMASK, &oldset, NULL);
    
    int cnt = 1;
    while(1){
      printf("child:%d\n", getpid());
      kill(getppid(), SIGUSR1);
      sleep(1);
    }
  }
  else{
    sigset_t st, oldset;
    sigemptyset(&st);
    sigaddset(&st, SIGUSR1);
    sigprocmask(SIG_BLOCK, &st, &oldset);

  
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = fcat;

    sigaction(SIGUSR1, &act, NULL);
    sigprocmask(SIG_SETMASK, &oldset, NULL);
    
    int cnt = 0;
    while(1){
      printf("parent:%d\n", getpid());
      kill(pid, SIGUSR2);
      sleep(1);
    }
  }
}

c/c++ 學習互助QQ羣:877684253

本人微信:xiaoshitou5854

相關文章
相關標籤/搜索