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

一、信号的介绍

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

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

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


二、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. perror("Fail to fork");
  11. exit(EXIT_FAILURE);
  12. }else if(pid == 0){
  13. while(1);
  14. }else{
  15. int signum;
  16. while(scanf("%d",&signum) == 1)
  17. {
  18. kill(pid,signum);
  19. system("ps -aux | grep a.out");
  20. }
  21. }
  22. return 0;
  23. }

运行结果如下:

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. for(i = 0;i < 5;i ++)
  25. {
  26. printf("child loop.\n");
  27. sleep(1);
  28. }
  29. }else{
  30. for(i = 0;i < 5;i ++)
  31. {
  32. printf("Father loop.\n");
  33. sleep(2);
  34. }
  35. }
  36. exit(EXIT_SUCCESS);
  37. }

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. sleep(2);
  20. n = alarm(5);
  21. printf("n = %d.\n",n);
  22. while(1)
  23. {
  24. printf("count = %d.\n", ++count);
  25. sleep(1);
  26. }
  27. return 0;
  28. }

运行结果如下:

案例二、综合案例

使用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. kill(getppid(),SIGKILL);
  23. usleep(500);
  24. exit(EXIT_SUCCESS);
  25. }
  26. flag = 1;
  27. break;
  28. case SIGINT:
  29. printf("The alarm is cancel.\n");
  30. alarm(0);
  31. break;
  32. }
  33. }
  34. int child_recv_fifo(char *fifo_name)
  35. {
  36. int n,fd;
  37. char buf[MAX];
  38. if((fd = open(fifo_name,O_RDONLY)) < 0)
  39. {
  40. fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
  41. return -1;
  42. }
  43. signal(SIGALRM,signal_handler);
  44. signal(SIGINT,signal_handler);
  45. alarm(15);//璁剧疆瀹氭椂鍣?
  46. while(1)
  47. {
  48. n = read(fd,buf,sizeof(buf));
  49. buf[n] = ‘\0‘;
  50. printf("Read %d bytes : %s.\n",n,buf);
  51. if(strncmp(buf,"quit",4) == 0 || n == 0)
  52. {
  53. kill(getppid(),SIGKILL);
  54. usleep(500);
  55. exit(EXIT_SUCCESS);
  56. }
  57. alarm(15);
  58. }
  59. return 0;
  60. }
  61. int father_send_fifo(char *fifo_name,int pid)
  62. {
  63. int n,fd;
  64. char buf[MAX];
  65. if((fd = open(fifo_name,O_WRONLY)) < 0)
  66. {
  67. fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
  68. return -1;
  69. }
  70. signal(SIGINT,SIG_IGN);
  71. while(1)
  72. {
  73. getchar();
  74. printf(">");
  75. fgets(buf,sizeof(buf),stdin);
  76. buf[strlen(buf)-1] = ‘\0‘;
  77. write(fd,buf,strlen(buf));
  78. if(strncmp(buf,"quit",4) == 0)
  79. {
  80. kill(pid,SIGKILL);
  81. usleep(500);
  82. exit(EXIT_SUCCESS);
  83. }
  84. }
  85. return 0;
  86. }
  87. int main(int argc,char *argv[])
  88. {
  89. int pid;
  90. if(argc < 3)
  91. {
  92. fprintf(stderr,"usage %s argv[1].\n",argv[0]);
  93. exit(EXIT_FAILURE);
  94. }
  95. if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
  96. {
  97. perror("Fail to mkfifo");
  98. exit(EXIT_FAILURE);
  99. }
  100. if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
  101. {
  102. perror("Fail to mkfifo");
  103. exit(EXIT_FAILURE);
  104. }
  105. if((pid = fork()) < 0)
  106. {
  107. perror("Fail to fork");
  108. exit(EXIT_FAILURE);
  109. }else if(pid == 0){
  110. child_recv_fifo(argv[2]);
  111. }else{
  112. father_send_fifo(argv[1],pid);
  113. }
  114. exit(EXIT_SUCCESS);
  115. }

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. kill(getppid(),SIGKILL);
  23. usleep(500);
  24. exit(EXIT_SUCCESS);
  25. }
  26. flag = 1;
  27. break;
  28. case SIGINT:
  29. printf("The alarm is cancel.\n");
  30. alarm(0);
  31. break;
  32. }
  33. }
  34. int child_recv_fifo(char *fifo_name)
  35. {
  36. int n,fd;
  37. char buf[MAX];
  38. if((fd = open(fifo_name,O_RDONLY)) < 0)
  39. {
  40. fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
  41. return -1;
  42. }
  43. signal(SIGALRM,signal_handler);
  44. signal(SIGINT,signal_handler);
  45. alarm(15);//璁剧疆瀹氭椂鍣?
  46. while(1)
  47. {
  48. n = read(fd,buf,sizeof(buf));
  49. buf[n] = ‘\0‘;
  50. printf("Read %d bytes : %s.\n",n,buf);
  51. if(strncmp(buf,"quit",4) == 0 || n == 0)
  52. {
  53. kill(getppid(),SIGKILL);
  54. usleep(500);
  55. exit(EXIT_SUCCESS);
  56. }
  57. alarm(15);
  58. }
  59. return 0;
  60. }
  61. int father_send_fifo(char *fifo_name,int pid)
  62. {
  63. int n,fd;
  64. char buf[MAX];
  65. if((fd = open(fifo_name,O_WRONLY)) < 0)
  66. {
  67. fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
  68. return -1;
  69. }
  70. signal(SIGINT,SIG_IGN);
  71. while(1)
  72. {
  73. getchar();
  74. printf(">");
  75. fgets(buf,sizeof(buf),stdin);
  76. buf[strlen(buf)-1] = ‘\0‘;
  77. write(fd,buf,strlen(buf));
  78. if(strncmp(buf,"quit",4) == 0)
  79. {
  80. kill(pid,SIGKILL);
  81. usleep(500);
  82. exit(EXIT_SUCCESS);
  83. }
  84. }
  85. return 0;
  86. }
  87. int main(int argc,char *argv[])
  88. {
  89. int pid;
  90. if(argc < 3)
  91. {
  92. fprintf(stderr,"usage %s argv[1].\n",argv[0]);
  93. exit(EXIT_FAILURE);
  94. }
  95. if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
  96. {
  97. perror("Fail to mkfifo");
  98. exit(EXIT_FAILURE);
  99. }
  100. if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
  101. {
  102. perror("Fail to mkfifo");
  103. exit(EXIT_FAILURE);
  104. }
  105. if((pid = fork()) < 0)
  106. {
  107. perror("Fail to fork");
  108. exit(EXIT_FAILURE);
  109. }else if(pid == 0){
  110. child_recv_fifo(argv[1]);
  111. }else{
  112. father_send_fifo(argv[2],pid);
  113. }
  114. exit(EXIT_SUCCESS);
  115. }

D.将进程挂起函数pause

解释如下:

案例如下:

阅读(31) | 评论(0) | 转发(0) |

0

上一篇:进程间通信---共享内存

下一篇:linux 进程(二) --- 进程的创建及相关api

相关热门文章

  • linux 常见服务端口
  • xmanager 2.0 for linux配置
  • 【ROOTFS搭建】busybox的httpd...
  • openwrt中luci学习笔记
  • 什么是shell

热门推荐

    -->

    给主人留下些什么吧!~~

    评论热议

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

    时间: 2024-08-01 17:53:20

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

    进程间通信--信号

    信号 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的. 信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达. 信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了. 信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息. 信号来源 信号事件的发生有两个来源: 硬件来源(比如我们按下了键盘或者其它硬件故障): 软件

    Linux进程间通信——信号

    一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中的进程捕获到这个信号然后作出一定的操作并最终被终止. 信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动.通常信号是由一个错误产生的.但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程.一个信号的产生叫生成,接收到一个信号

    进程间通信——信号

    进程间通信--信号 宗旨:技术的学习是有限的,分享的精神的无限的. 一.信号和中断 1.信号基本概念 (1)发送信号:产生信号,有多种发送信号的方式[一个进程到另一个进程,内核向用户,进程向自己] (2)安装信号:设置信号到来时不再执行默认操作,而是执行自定义的代码. (3)递送信号:一个信号被操作系统发送到目标进程引起某段处理程序的执行. (4)捕获信号:被递送的信号在目标进程引起某段处理程序的执行. (5)屏蔽信号:进程告诉操作系统暂时不接受某些信号. (6)忽略信号:进程被递送到目标进程,

    详解linux进程间通信-信号

    前言:之前说看<C++ Primer >暂时搁浅一下,迷上公司大神写的代码,想要明白,主要是socket.进程间通信! 知道进程间通信:信号.信号量.管道.消息队列.共享内存(共享存储),也能写些简单代码进行通信,但不知道应用在哪?感觉很多小伙伴跟我有类似经历吧? 一.应用实例: 要去linux设备上去添加.改密用户:本地去linux设备添加用户,用socket实现,其实大神改的ssh源码来实现的,这不是我要讲的重点,而我要讲的是准备过程,去登陆linux设备,要准备好:管理员.密码等. 简略

    第81课 信号与槽的连接方式

    1. QObject::connect函数 bool connect(const QObject* sender, //发送者 const char* signal, //信号 const QObject* receiver, //接收者 const char* method, //槽函数 Qt::ConnectionType type = Qt::AutoConnection);//连接方式 2. 深入信号与槽的连接方式 (1)Qt::DirectConnection(直接连接,立即调用):直

    五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O

    五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O 五种I/O 模式:[1]        阻塞 I/O           (Linux下的I/O操作默认是阻塞I/O,即open和socket创建的I/O都是阻塞I/O)[2]        非阻塞 I/O        (可以通过fcntl或者open时使用O_NONBLOCK参数,将fd设置为非阻塞的I/O)[3]        I/O 多路复用     (I/O

    异步方式

    异步方式是为了请求客户端请求的方式. 同步机制:是指发送方发送请求后,需要等待接受请求方的回应后才能发起下一个请求; 异步机制:是指发送方发送发送请求后,不需要等待接受请求方的回应便可发送下一个请求. 阻塞方式:实质是指I/O流的操作,在请求结果返回之前该线程处于被挂起的状态,一直等到调用结束后在处于挂起的状态. 非阻塞方式:在I/O操作中,如果不能马上返回结果,当前的线程也不会被挂起,而是立即执行下一个调用. 同步阻塞方式:发送方向接受方发送请求后一直等待相应,接收方处理请求时进行的I/O操作

    MVC4 Action 两种异步方式

    1. xxxAsync/xxxCompleted 组合方式异步,xxxCompleted  就是他的回调函数,在执行完 xxxAsync 后调用xxxCompleted  . 使用 异步方式必须继承AsyncController 控制器: Async 异步方式通过 Controller的属性 AsyncManager 进行赋值 回调,看下面代码: public class AppController : AsyncController { public void ArticleAsync(str

    iOS网络编程——Day4(使用NSURLConnection异步方式获取数据)

    XCode7使用HTTPS通信,在info.plist文件中加一个属性@{@"NSAppTransportSecurity":@{@"NSAllowsArbitraryLoads":YES}} 接口网站:http://www.webxml.com.cn xml  国际标准 json 流行标准 get: 使用简单,接口外露不安全 //QQ在线查询的接口宏定义GET#define QQOnlineURL_GET @"http://webservice.webx