进程间通信--信号(进程间通信唯一的异步方式)
来源:互联网 发布:星宇免费记账软件 编辑:程序博客网 时间:2024/05/19 20:42
转载地址:
http://blog.chinaunix.net/uid-25120309-id-3301181.html向原作者致敬^ _ ^
正文:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、信号的介绍
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。
二、linux操作系统支持的信号
A. kill -l
B.常用信号的含义
C.闹钟函数alarm
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.通过系统调用向一个指定的进程发送信号
#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);参数说明:
第一个参数:指定发送信号的接收线程
第二个参数:信号的signum
案例一、
父进程从终端输入signum,然后发给子进程
#include <stdio.h>#include <sys/types.h>#include <signal.h>#include <stdlib.h>int main(){ int pid; if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); }else if(pid == 0){ while(1); }else{ int signum; while(scanf("%d",&signum) == 1) { kill(pid,signum); system("ps -aux | grep a.out"); } } return 0;}运行结果如下:
[root@zx signal]# ./a.out 19Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQroot 8313 0.0 0.0 1732 364 pts/1 S+ 19:50 0:00 ./a.outroot 8314 8.7 0.0 1728 72 pts/1 T+ 19:50 0:00 ./a.outroot 8315 0.0 0.1 6208 1012 pts/1 S+ 19:50 0:00 sh -c ps -aux | grep a.outroot 8317 0.0 0.1 5612 744 pts/1 S+ 19:50 0:00 grep a.out9Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQroot 8313 0.0 0.0 1732 364 pts/1 S+ 19:50 0:00 ./a.outroot 8314 5.0 0.0 0 0 pts/1 Z+ 19:50 0:00 [a.out] <defunct>root 8318 0.0 0.1 6208 1012 pts/1 S+ 19:50 0:00 sh -c ps -aux | grep a.outroot 8320 0.0 0.1 5612 740 pts/1 S+ 19:50 0:00 grep a.out18Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQroot 8313 0.0 0.0 1732 364 pts/1 S+ 19:50 0:00 ./a.outroot 8314 1.9 0.0 0 0 pts/1 Z+ 19:50 0:00 [a.out] <defunct>root 8321 0.0 0.1 6208 1016 pts/1 S+ 19:51 0:00 sh -c ps -aux | grep a.outroot 8323 0.0 0.1 5612 744 pts/1 S+ 19:51 0:00 grep a.out^C[root@zx signal]#
B.捕捉一个信号
对应的API
对应的API
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);其原型:
typedef void (*sighandler_t)(int);
参数说明:
signum : 指定信号
handler : SIG_IGN忽略该信号,SIG_DFL采用系统默认方式处理信号,自定义的信号处理函数指针。
案例探究:
通过异步方式,给子进程收尸
注意:子进程在终止时会给父进程发SIGCHLD,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需要专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
#include <stdio.h>#include <signal.h>#include <unistd.h>#include <stdlib.h>void child_exit_handler(int signum){ if(signum == SIGCHLD) { printf("Child exit.\n"); wait(NULL); }}int main(){ int pid; int i = 0; //想内核注册,处理 SIGCHLD信号的方式 signal(SIGCHLD,child_exit_handler); if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); }else if(pid == 0){ for(i = 0;i < 5;i ++) { printf("child loop.\n"); sleep(1); } }else{ for(i = 0;i < 5;i ++) { printf("Father loop.\n"); sleep(2); } } exit(EXIT_SUCCESS);}
C.闹钟函数alarm
#include <unistd.h> unsigned int alarm(unsigned int seconds);alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号。
seconds:指定的秒数,如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。
成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则放回上一个闹钟时间的剩余时间,否则返回0。
alarm(100);
........
......
alarm(5);
出错:-1
案例探究:
案例二、综合案例
#include <stdio.h>#include <signal.h>#include <stdlib.h>void handler(int signum){ if(signum == SIGALRM) { printf("Recv SIGALARM.\n"); } exit(EXIT_SUCCESS);}int main(){ int count = 0; int n = 0; signal(SIGALRM,handler); n = alarm(10); printf("n = %d.\n",n); sleep(2); n = alarm(5); printf("n = %d.\n",n); while(1) { printf("count = %d.\n", ++count); sleep(1); } return 0;}
案例二、综合案例
使用FIFO实现clientA与clientB之间聊天
A.输入quit后,两个进程退出
B.如果在20秒内,没有等到另一端发来的消息,则认为对方已不在,此时终止。
clientA:(与clientB只是在对fifo a和b的调用上相反)
client B
D.将进程挂起函数pause
#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <string.h>#include <fcntl.h>#define MAX 100void signal_handler(int signum){ static int flag = 0; switch(signum) { case SIGALRM: if(flag == 0) { printf("The people is leaving,the system is closed in 10 seconds \ and you can input 'ctrl + c' cancel.\n"); alarm(10); }else{ kill(getppid(),SIGKILL); usleep(500); exit(EXIT_SUCCESS); } flag = 1; break; case SIGINT: printf("The alarm is cancel.\n"); alarm(0); break; }}int child_recv_fifo(char *fifo_name){ int n,fd; char buf[MAX]; if((fd = open(fifo_name,O_RDONLY)) < 0) { fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno)); return -1; } signal(SIGALRM,signal_handler); signal(SIGINT,signal_handler); alarm(15); while(1) { n = read(fd,buf,sizeof(buf)); buf[n] = '\0'; printf("Read %d bytes : %s.\n",n,buf); if(strncmp(buf,"quit",4) == 0 || n == 0) { kill(getppid(),SIGKILL); usleep(500); exit(EXIT_SUCCESS); } alarm(15); } return 0;}int father_send_fifo(char *fifo_name,int pid){ int n,fd; char buf[MAX]; if((fd = open(fifo_name,O_WRONLY)) < 0) { fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno)); return -1; } signal(SIGINT,SIG_IGN); while(1) { getchar(); printf(">"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; write(fd,buf,strlen(buf)); if(strncmp(buf,"quit",4) == 0) { kill(pid,SIGKILL); usleep(500); exit(EXIT_SUCCESS); } } return 0;}int main(int argc,char *argv[]){ int pid; if(argc < 3) { fprintf(stderr,"usage %s argv[1].\n",argv[0]); exit(EXIT_FAILURE); } if(mkfifo(argv[1],0666) < 0 && errno != EEXIST) { perror("Fail to mkfifo"); exit(EXIT_FAILURE); } if(mkfifo(argv[2],0666) < 0 && errno != EEXIST) { perror("Fail to mkfifo"); exit(EXIT_FAILURE); } if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); }else if(pid == 0){ child_recv_fifo(argv[2]); }else{ father_send_fifo(argv[1],pid); } exit(EXIT_SUCCESS);}
client B
#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <string.h>#include <fcntl.h>#define MAX 100void signal_handler(int signum){ static int flag = 0; switch(signum) { case SIGALRM: if(flag == 0) { printf("The people is leaving,the system is closed in 10 seconds \ and you can input 'ctrl + c' cancel.\n"); alarm(10); }else{ kill(getppid(),SIGKILL); usleep(500); exit(EXIT_SUCCESS); } flag = 1; break; case SIGINT: printf("The alarm is cancel.\n"); alarm(0); break; }}int child_recv_fifo(char *fifo_name){ int n,fd; char buf[MAX]; if((fd = open(fifo_name,O_RDONLY)) < 0) { fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno)); return -1; } signal(SIGALRM,signal_handler); signal(SIGINT,signal_handler); alarm(15); while(1) { n = read(fd,buf,sizeof(buf)); buf[n] = '\0'; printf("Read %d bytes : %s.\n",n,buf); if(strncmp(buf,"quit",4) == 0 || n == 0) { kill(getppid(),SIGKILL); usleep(500); exit(EXIT_SUCCESS); } alarm(15); } return 0;}int father_send_fifo(char *fifo_name,int pid){ int n,fd; char buf[MAX]; if((fd = open(fifo_name,O_WRONLY)) < 0) { fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno)); return -1; } signal(SIGINT,SIG_IGN); while(1) { getchar(); printf(">"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; write(fd,buf,strlen(buf)); if(strncmp(buf,"quit",4) == 0) { kill(pid,SIGKILL); usleep(500); exit(EXIT_SUCCESS); } } return 0;}int main(int argc,char *argv[]){ int pid; if(argc < 3) { fprintf(stderr,"usage %s argv[1].\n",argv[0]); exit(EXIT_FAILURE); } if(mkfifo(argv[1],0666) < 0 && errno != EEXIST) { perror("Fail to mkfifo"); exit(EXIT_FAILURE); } if(mkfifo(argv[2],0666) < 0 && errno != EEXIST) { perror("Fail to mkfifo"); exit(EXIT_FAILURE); } if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); }else if(pid == 0){ child_recv_fifo(argv[1]); }else{ father_send_fifo(argv[2],pid); } exit(EXIT_SUCCESS);}
D.将进程挂起函数pause
#include <unistd.h> int pause(void);解释如下:
The pause() library function causes the invoking process (or thread) to sleep until a signal is received that either terminates it or causes it to call a signal-catching function.
PS:(利用signal模拟中断)
利用signal可以在用户态实现简单的中断:如主进程在执行一项工作,但是主进程希望某一文件非空的时候立马处理其他工作,这个和硬件中断很相似
其实,这个就可以用signal来实现:主进程开一个子进程用来阻塞读取指定文件,若非空,则向父进程发送kill()命令,以通知其异步工作,而父进程处理signal的handler函数之后还会返回之前的调用点继续工作,这个和硬件中断真是很像!
linux下模拟这个硬件中断很方便哦~^ _ ^
- 进程间通信--信号(进程间通信唯一的异步方式)
- 进程间通信--信号(进程间通信唯一的异步方式)
- 进程间通信--信号(进程间通信唯一的异步方式)
- 进程间通信--信号(进程间通信唯一的异步方式)
- 进程间通信--信号(进程间通信唯一的异步方式)
- 进程间通信--信号(进程间通信唯一的异步方式)
- Linux进程间的通信方式:信号
- 信号--进程间的通信
- 进程间的信号通信
- 进程间的 信号 通信
- 进程间的信号通信
- 进程间的信号通信
- 信号通信(进程间通信)
- 进程间的通信之信号通信
- 进程间通信-信号
- 进程间通信--信号
- 进程间通信--信号
- 进程间通信-信号
- c++中的虚函数和多态之间的关系
- 关于引用数据类型与基本数据类型的认识
- 【win7快捷方式小箭头怎么去掉】
- android 将webview作为插件播放swf 只显示部分画面的问题
- ibatis TIMESTAMP的异常解决方法
- 进程间通信--信号(进程间通信唯一的异步方式)
- 通过Type.InvokeMethod实现方法的重载
- 十种不同类型的社交网站界面设计
- Windows安装Bundler
- 【XAML】StaticResource 与 DynamicResource 的区别
- NPN传感器PNP传感器原理和分类
- 词法分析器的实现
- iOS中策略模式初运用
- SqlServer中char及Varchar,nVarChar和Oracle中varchar2区别及其联系