進程間通信---信號

一、信號的介紹


信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式。

信號可以直接進行用戶空間進程和內核進程之間的交互,內核進程也可以利用它來通知用戶空間進程發生了那些系統事件。

如果該進程當前並未處於執行態,則該信號就由內核保存起來,直到該進程恢復執行再傳遞個它;如果一個信號被進程設置爲阻塞,則該信號的傳遞被延遲,直到其阻塞取消時才被傳遞給進程。

二、linux操作系統支持的信號

A. kill  -l



B.常用信號的含義





三、信號的產生

A.用戶在終端按下某些鍵時,終端驅動程序會發送信號給前臺進程,例如ctr+c產生SIGINT,  ctr + \產生SIGQUI信號,ctr + z產生SIGTSTP。

B.硬件異常產生信號,這些條件由硬件檢測到並通知內核,然後內核向當前進程發送適當的信號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋爲SIGFPE信號發送給進程。再比如當前進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋爲SIGSEGV信號發送給當前進程 。

C.一個進程調用int kill(pid_t pid,int sig)函數可以給另一個進程發送信號

D.可以用kill命令給某個進程發送信號,如果不明確指定信號則發送SIGTERM信號,該信號的默認處理動作是終止進程。

E.當內核檢測到某種軟件條件發生時也可以通過信號通知進程,例如鬧鐘超時產生SIGALRM信號,向讀端已關閉的管道寫數據時產生SIGPIPE信號。

四、進程對信號的處理

A.忽略此信號
B.執行該信號的默認處理動作
C.提供一個信號處理函數,要求內核在處理該信號時切換到用戶態執行這個處理函數,這種方式成爲捕捉(Catch)一個信號。
 

五、相關信號API

A.通過系統調用向一個指定的進程發送信號



參數說明:
第一個參數:指定發送信號的接收線程
第二個參數:信號的signum

案例一、
父進程從終端輸入signum,然後發給子進程


點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <signal.h>
  4. #include <stdlib.h>

  5. int main()
  6. {
  7.     int pid;

  8.     if((pid = fork()) < 0)
  9.     {
  10.     
  11.         perror("Fail to fork");
  12.         exit(EXIT_FAILURE);
  13.     
  14.     }else if(pid == 0){
  15.         
  16.         while(1);
  17.     
  18.     }else{
  19.         
  20.         int signum;
  21.         
  22.         while(scanf("%d",&signum) == 1)
  23.         {
  24.             kill(pid,signum);
  25.             system("ps -aux | grep a.out");
  26.         }
  27.     }

  28.     return 0;
  29. }

運行結果如下:



B.捕捉一個信號



對應的API



其原型:



我們一般都是用第一個,也就是通過typedef改寫過的。

注意:signal函數我一般認爲其是向內核註冊當前進程收到信號的處理的方式。
signal(SIGINT,handler);


參數說明:

signum  :  指定信號
handler  :  SIG_IGN忽略該信號,SIG_DFL採用系統默認方式處理信號,自定義的信號處理函數指針。

案例探究:

通過異步方式,給子進程收屍

注意:子進程在終止時會給父進程發SIGCHLD,該信號的默認處理動作是忽略,父進程可以自定義SIGCHLD信號的處理函數,這樣父進程只需要專心處理自己的工作,不必關心子進程了,子進程終止時會通知父進程,父進程在信號處理函數中調用wait清理子進程即可。


點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>

  5. void child_exit_handler(int signum)
  6. {
  7.     if(signum == SIGCHLD)
  8.     {
  9.         printf("Child exit.\n");
  10.         wait(NULL);
  11.     }
  12. }

  13. int main()
  14. {
  15.     int pid;
  16.     int i = 0;

  17.     //想內核註冊,處理 SIGCHLD信號的方式
  18.     signal(SIGCHLD,child_exit_handler);

  19.     if((pid = fork()) < 0)
  20.     {
  21.         perror("Fail to fork");
  22.         exit(EXIT_FAILURE);

  23.     }else if(pid == 0){
  24.         
  25.         for(i = 0;i < 5;i ++)
  26.         {
  27.             printf("child loop.\n");
  28.             sleep(1);
  29.         }
  30.     
  31.     }else{
  32.         
  33.         for(i = 0;i < 5;i ++)
  34.         {
  35.             printf("Father loop.\n");
  36.             sleep(2);
  37.         }

  38.     }

  39.     exit(EXIT_SUCCESS);
  40. }

C.鬧鐘函數alarm



larm()也稱爲鬧鐘函數,它可以在進程中設置一個定時器。當定時器指定的時間到時,內核就向進程發送SIGALARM信號。

seconds:指定的秒數,如果參數seconds爲0,則之前設置的鬧鐘會被取消,並將剩下的時間返回。

成功:如果調用此alarm()前,進程中已經設置了鬧鐘時間,則放回上一個鬧鐘時間的剩餘時間,否則返回0。

alarm(100);
........

......

alarm(5);

出錯:-1

案例探究:


點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <stdlib.h>

  4. void handler(int signum)
  5. {
  6.     if(signum == SIGALRM)
  7.     {
  8.         printf("Recv SIGALARM.\n");
  9.     }

  10.     exit(EXIT_SUCCESS);
  11. }

  12. int main()
  13. {
  14.     int count = 0;
  15.     int n = 0;

  16.     signal(SIGALRM,handler);

  17.     n = alarm(10);

  18.     printf("n = %d.\n",n);
  19.     
  20.     sleep(2);

  21.     n = alarm(5);

  22.     printf("n = %d.\n",n);
  23.     
  24.     while(1)
  25.     {
  26.         printf("count = %d.\n", ++count);
  27.         sleep(1);
  28.     }

  29.     return 0;
  30. }
運行結果如下:



案例二、綜合案例

使用FIFO實現clientA與clientB之間聊天
A.輸入quit後,兩個進程退出
B.如果在20秒內,沒有等到另一端發來的消息,則認爲對方已不在,此時終止。

clientA:


點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <fcntl.h>

  9. #define MAX 100

  10. void signal_handler(int signum)
  11. {
  12.     static int flag = 0;

  13.     switch(signum)
  14.     {
  15.         case SIGALRM:
  16.             if(flag == 0)
  17.             {
  18.                 printf("The people is leaving,the system is closed in 10 seconds \
  19.                         and you can input 'ctrl + c' cancel.\n");
  20.                 alarm(10);
  21.             }else{
  22.                 
  23.                 kill(getppid(),SIGKILL);
  24.                 usleep(500);
  25.                 exit(EXIT_SUCCESS);
  26.             }

  27.             flag = 1;            
  28.             break;

  29.         case SIGINT:
  30.             printf("The alarm is cancel.\n");
  31.             alarm(0);
  32.             break;
  33.     }

  34. }

  35. int child_recv_fifo(char *fifo_name)
  36. {
  37.     int n,fd;
  38.     char buf[MAX];

  39.     if((fd = open(fifo_name,O_RDONLY)) < 0)
  40.     {
  41.         fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
  42.         return -1;
  43.     }

  44.     signal(SIGALRM,signal_handler);
  45.     signal(SIGINT,signal_handler);
  46.     alarm(15);//璁劇疆瀹氭椂鍣?    
  47.     while(1)
  48.     {
  49.         n = read(fd,buf,sizeof(buf));
  50.         buf[n] = '\0';

  51.         printf("Read %d bytes : %s.\n",n,buf);

  52.         if(strncmp(buf,"quit",4) == 0 || n == 0)
  53.         {
  54.             kill(getppid(),SIGKILL);
  55.             usleep(500);
  56.             exit(EXIT_SUCCESS);
  57.         }

  58.         alarm(15);
  59.     }

  60.     return 0;
  61. }

  62. int father_send_fifo(char *fifo_name,int pid)
  63. {
  64.     int n,fd;
  65.     char buf[MAX];

  66.     if((fd = open(fifo_name,O_WRONLY)) < 0)
  67.     {
  68.         fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
  69.         return -1;
  70.     }

  71.     signal(SIGINT,SIG_IGN);

  72.     while(1)
  73.     {
  74.         getchar();
  75.         printf(">");

  76.         fgets(buf,sizeof(buf),stdin);
  77.         buf[strlen(buf)-1] = '\0';

  78.         write(fd,buf,strlen(buf));

  79.         if(strncmp(buf,"quit",4) == 0)
  80.         {
  81.             kill(pid,SIGKILL);
  82.             usleep(500);
  83.             exit(EXIT_SUCCESS);
  84.         }
  85.     }

  86.     return 0;
  87. }

  88. int main(int argc,char *argv[])
  89. {
  90.     int pid;

  91.     if(argc < 3)
  92.     {
  93.         fprintf(stderr,"usage %s argv[1].\n",argv[0]);
  94.         exit(EXIT_FAILURE);
  95.     }

  96.     if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
  97.     {
  98.         perror("Fail to mkfifo");
  99.         exit(EXIT_FAILURE);
  100.     }

  101.     if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
  102.     {
  103.         perror("Fail to mkfifo");
  104.         exit(EXIT_FAILURE);
  105.     }
  106.     
  107.     if((pid = fork()) < 0)
  108.     {
  109.     
  110.         perror("Fail to fork");
  111.         exit(EXIT_FAILURE);
  112.     
  113.     }else if(pid == 0){
  114.         
  115.         child_recv_fifo(argv[2]);
  116.     
  117.     }else{

  118.         father_send_fifo(argv[1],pid);
  119.     }

  120.     exit(EXIT_SUCCESS);
  121. }

client B


點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <fcntl.h>

  9. #define MAX 100

  10. void signal_handler(int signum)
  11. {
  12.     static int flag = 0;

  13.     switch(signum)
  14.     {
  15.         case SIGALRM:
  16.             if(flag == 0)
  17.             {
  18.                 printf("The people is leaving,the system is closed in 10 seconds \
  19.                         and you can input 'ctrl + c' cancel.\n");
  20.                 alarm(10);
  21.             }else{
  22.                 
  23.                 kill(getppid(),SIGKILL);
  24.                 usleep(500);
  25.                 exit(EXIT_SUCCESS);
  26.             }

  27.             flag = 1;            
  28.             break;

  29.         case SIGINT:
  30.             printf("The alarm is cancel.\n");
  31.             alarm(0);
  32.             break;
  33.     }

  34. }

  35. int child_recv_fifo(char *fifo_name)
  36. {
  37.     int n,fd;
  38.     char buf[MAX];

  39.     if((fd = open(fifo_name,O_RDONLY)) < 0)
  40.     {
  41.         fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
  42.         return -1;
  43.     }

  44.     signal(SIGALRM,signal_handler);
  45.     signal(SIGINT,signal_handler);
  46.     alarm(15);//璁劇疆瀹氭椂鍣?    
  47.     while(1)
  48.     {
  49.         n = read(fd,buf,sizeof(buf));
  50.         buf[n] = '\0';

  51.         printf("Read %d bytes : %s.\n",n,buf);

  52.         if(strncmp(buf,"quit",4) == 0 || n == 0)
  53.         {
  54.             kill(getppid(),SIGKILL);
  55.             usleep(500);
  56.             exit(EXIT_SUCCESS);
  57.         }

  58.         alarm(15);
  59.     }

  60.     return 0;
  61. }

  62. int father_send_fifo(char *fifo_name,int pid)
  63. {
  64.     int n,fd;
  65.     char buf[MAX];

  66.     if((fd = open(fifo_name,O_WRONLY)) < 0)
  67.     {
  68.         fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
  69.         return -1;
  70.     }

  71.     signal(SIGINT,SIG_IGN);

  72.     while(1)
  73.     {
  74.         getchar();
  75.         printf(">");

  76.         fgets(buf,sizeof(buf),stdin);
  77.         buf[strlen(buf)-1] = '\0';

  78.         write(fd,buf,strlen(buf));

  79.         if(strncmp(buf,"quit",4) == 0)
  80.         {
  81.             kill(pid,SIGKILL);
  82.             usleep(500);
  83.             exit(EXIT_SUCCESS);
  84.         }
  85.     }

  86.     return 0;
  87. }

  88. int main(int argc,char *argv[])
  89. {
  90.     int pid;

  91.     if(argc < 3)
  92.     {
  93.         fprintf(stderr,"usage %s argv[1].\n",argv[0]);
  94.         exit(EXIT_FAILURE);
  95.     }

  96.     if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
  97.     {
  98.         perror("Fail to mkfifo");
  99.         exit(EXIT_FAILURE);
  100.     }

  101.     if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
  102.     {
  103.         perror("Fail to mkfifo");
  104.         exit(EXIT_FAILURE);
  105.     }
  106.     
  107.     if((pid = fork()) < 0)
  108.     {
  109.     
  110.         perror("Fail to fork");
  111.         exit(EXIT_FAILURE);
  112.     
  113.     }else if(pid == 0){
  114.         
  115.         child_recv_fifo(argv[1]);
  116.     
  117.     }else{

  118.         father_send_fifo(argv[2],pid);
  119.     }

  120.     exit(EXIT_SUCCESS);
  121. }

D.將進程掛起函數pause



解釋如下:



案例如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章