进程间通信--信号(进程间通信唯一的异步方式)   

原文地址:进程间通信--信号(进程间通信唯一的异步方式) 作者:Deem_passion


一、信号的介绍


信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。


信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。


如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。


二、linux操作系统支持的信号


A. kill  -l

进程间通信--信号(进程间通信唯一的异步方式_通信

B.常用信号的含义

进程间通信--信号(进程间通信唯一的异步方式_linux_02

进程间通信--信号(进程间通信唯一的异步方式_检测_03

三、信号的产生


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.通过系统调用向一个指定的进程发送信号

进程间通信--信号(进程间通信唯一的异步方式_操作系统_04

参数说明:

第一个参数:指定发送信号的接收线程

第二个参数:信号的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. }


运行结果如下:

进程间通信--信号(进程间通信唯一的异步方式_通信_05

B.捕捉一个信号

进程间通信--信号(进程间通信唯一的异步方式_检测_06

对应的API

进程间通信--信号(进程间通信唯一的异步方式_程序_07

其原型:

进程间通信--信号(进程间通信唯一的异步方式_检测_08


我们一般都是用第一个,也就是通过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


进程间通信--信号(进程间通信唯一的异步方式_操作系统_09

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. }

运行结果如下:


进程间通信--信号(进程间通信唯一的异步方式_linux_10

案例二、综合案例


使用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

进程间通信--信号(进程间通信唯一的异步方式_操作系统_11

解释如下:


进程间通信--信号(进程间通信唯一的异步方式_通信_12

案例如下:

进程间通信--信号(进程间通信唯一的异步方式_程序_13