进程间通信--信号
来源:互联网 发布:windows下安装ansible 编辑:程序博客网 时间:2024/04/19 12:00
1、信号的作用
(1) 使用它来通知一个或者多个进程异步事件的发生
2、信号的发送方向
信号不但能从内核发往一个进程,也能从一个进程发往另一个进程。即:
(1) 内核-->进程
(2) 进程-->另一个进程
3、信号的使用范围
由信号特点决定:不能用信号来作进程间的直接数据传送,而把它用作对非正常情况的处理。
4、linux中信号的种类
信号类型在linux系统库中 bits/signum.h 中有定义
SIGHUP 内核-->进程。 当终止一个终端时,内核就把这一种信号发送给该终端所控制的所有进程
SIGINT 内核-->进程。 当用户按了中断键(一般为Ctrl+C后),内核就向与该终端关联的所有进程发送这种信号
SIGQUIT 内核-->进程。 当用户按了退出键时(一般为Ctrl+\后),内核就发出这种信号
SIGILL 内核-->进程。 当一个进程企图执行一条非法指令时,内核就会发出这种信号
SIGTRAP 由调试程序使用的专用信号
SIGFPE 内核-->进程。 当产生浮点错误时(比如溢出),内核就会发出这种信号
SIGKILL 进程-->进程。(内核偶尔也会发出这种信号)这是一个相当特殊的信号,它从一个进程发送到另一个进程,使接收到该信号的进程终止。SIGKILL特点:它不能被忽略和捕捉,只能通过用户定义的响应中断处理程序而处理该信号。因为所有的信号能被忽略和捕捉,所以只有这种信号能绝对保证终止一个进程。
SIGALRM 内核-->进程。 当一个定时起到的时候,内核就向该进程发出这种信号
SIGTERM 进程-->进程。 这种信号是有系统提供给普通用户使用,按照规定,它被用来终止一个进程
SIGTOP 这种信号使进程暂时中止运行,系统控制权转回正在等待运行的下一个进程
SIGUSR1 和 SIGUSR2 进程-->进程。和SIGTERM一样,这两种信号不是由内核发出的,可以用于用户做任何事情。
SIGCHLD 子进程结束信号。unix中用它来实现系统调用 exit()和 wait()。执行exit()函数时,就向父进程发送 SIGCHLD 信号。如果父进程正在执行 wait() ,则它被唤醒;若果父进程不是在执行wait() ,则父进程不会捕捉到SIGCHLD信号,因此信号不起作用。
5、信号的处理函数
这些信号定义在linux系统库 signal.h 文件中,函数如下:
int signal(int sig,_sighandler _t handler);
(1) 第1个参数: sig --> 指明了所要处理的信号类型,他可以取除了 SIGKILL 和 SIGTOP 外的任何一种信号。
(2)第2个参数:handler-->指向一个对信号sig处理的操作函数。当进程一旦接收到sig的信号时,就立刻执行handler所指定的函数,不管进程正在执行的那一部分程序。handler可以取值为:SIG_IGN 和 SIG_ DEL.
SIG_IGN: 这个符号表示武略此信号
SIG_DEL: 这个信号表示恢复系统对信号的默认处理
(3)函数返回值: 如果调用成功,函数返回进程被中断的那一点继续执行;若果失败,函数返回值是 SIG_ERR。
例子1:
进程忽略 SIGINT 信号
#include <stdlib.h>#include <stdio.h>#include <signal.h>int main(){signal(SIGINT,SIG_IGN); //告诉进程将SIGINT信号忽略printf("xixi\n);sleep(10);printf("end\n");return;}//如果在程序中需要重新恢复系统对该信号的缺省处理,就是用下面的语句:signal(SIGINT,SIG_DEL);在linux中常常利用SIG_INT 和 SIG_DEL 屏蔽SIGINT 和 SIGQUIT 来保证执行重要任务的程序不被中断。
例2:
#include <stdlib.h>#include <stdio.h>#include <signal.h>int catch(int sig)int main(){signal(SIGINT,catch);printf("xixi\n");sleep(10);return;}int catch(int sig){printf("Catch succeed!\n");return 1;}//当程序中按下中断键(Ctrl+C),函数catch就被执行。若果我们希望一个进程被信号终止前能够完成一些处理工作,如删除工作中使用的临时文件等,就可以设计一个信号处理函数完成工作。例如:int catch(int sig){printf("Catch succeed!\n");exit(1);}
思考:这里,在子程序中使用exit(1) 和 return 返回主程序的时候有什么区别呢????
例3:
signal()返回原先与指定信号相关联的处理函数,这样,我们就可以保存和恢复原来对指定信号的
处理动作。下面代码说明这一技术:int (*oldptr)newcath(); //设定SIGINT的关联,同时保存原来的关联oldptr=signal(SIGINT,newcatch); /*工作代码段*/............/*恢复原来的关联*/signal(SIGINT,oldptr);
6、信号和系统的调用关系
当一个进程正在执行一个系统调用的时候,如果向该进程发送一个信号,那么对于大多数系统调用而言,这个信号在系统调用完成之前将不起作用,因为这些系统调用不能被信号打断。但是有少数几个系统调用能被信号打断。例如:wait()、pause()以及read()、write()、open()等。如果一个系用调用被打断,它就返回-1。
下面代码说明这种情况:
if(write(tfd,buf,SIZE)<0){if(errno == EINTR){warn("Write interrupt");......}}
7、信号的复位
在linux中,当一个信号的信号处理函数执行时,如果进程又接收到了信号,则进行判断:
如果进程执行过程中,接收同一类型的信号,则信号处理函数不会被中断,直到信号处理函数执行完毕再中心调用相应的处理函数。
如果进程执行过程中,接收不同类型的信号,则中断信号处理函数,执行与之对应的信号处理函数,直到信号处理完毕,返回原来信号处理的函数。
(1)、进程执行过程当中,接收同一类型的信号
#include <signal.h>int interrupt(){printf("Interrupt called\n");sleep(3);printf("Interrupt Func Ended\n");}int main(){signal(SIGINT,interrupt);printf("Interrrupt set for SIGINT\n");sleep(10);printf("program normal ended\n");return;}执行结果interrupt set for SIGINT<ctrl+c>Interrupt called <ctrl+c>Interrupt Func Endedprogram normal ended
(2)、进程执行过程当中,接收不同类型的信号
#include <signal.h>int interrupt(){printf("Interrupt called\n");sleep(3);printf("Interrupt Func Ended\n");}int catchquit(){printf("Quit called\n");sleep(3);printf("Quit ended\n);}int main(){signal(SIGINT,interrupt);signal(SIGQUIT,catchquit);printf("Interrupt set for SIGINT\n");sleep(10);printf("Program NORMAL ended\n");return;}执行结果:Interrupt set for SIGINT<ctrl+c>Interrupt calledQuit calledQuit calledInterrupt Func EndedProgram NORMAL ended
8、在进程间发送信号
一个进程通过对 signal() 的调用来处理其它进程发送来的信号。同时,一个进程也可以向其它的进程发送信号。这一操作由系统调用kill()来完成kill()函数在linux系统库signal.h中的函数声明:
int kill(pid_t pid,int sig);
例子1:
下面是一个使用kill()调用的例子。这个程序建立连个进程,并通过向对方发送信号SIGUSR1来实现他们之间的同步。这两个进程都处于一个死循环,在对方发送信号之前,都处于暂停等待中。这是通过pause()来实现的,它能够使一个程序暂停,直到一个信号到达,然后输出信息,并用kill发送一个信号给对方。当用户按了按键中断,这两个进程都将终止。
#include <signal.h>int ntimes=0;P_action(){printf("parent caught signal #%d\n",++ntimes);}c_action(){printf("child caught signal #%d\n",++ntimes);main(){int pid,ppid;int p_action,c_action;signal(SIGUSR1,P_action);switch(pid = fock()){case -1:perror("synchro");exit(1);case 0:signal(SIGUSR1,c_action);ppid = getppid();for(;;){sleep(1);kill(ppid,SIGUSR1);pause();}break;default:for(;;){pause();sleep(1);kill(pid,SIGUSR1);}}}程序运行结果是:patent caught signal #1child caught signal #1patent caught signal #2child caught signal #2patent caught signal #3child caught signal #3patent caught signal #4child caught signal #4<ctrl+c>(1)、为什么父子进程同时打印出同一个“数字”????
答:通过向对方发送信号SIGUSR1来实现父子进程同步,所以父子进程是同步运行。
(2)、为什么父子进程在按下<ctrl+c> 后同时终止呢 ??
答:按下 <ctrl+c> 后,父子进程同时发送kill信号给对方,所以父子进程同时终止
(3)、kill一般用于父子进程之间??
答:因为调用kill()的进程需要知道信号发往的进程的标识符,所以这种信号的发送通常在关系密切的进程之间进行,比如父子进程。
9、信号和系统调用alarm()和pause()之间的关系
alarm()函数:
(1)、alarm()函数在系统调用linux系统库unistd.h中的函数中声明如下:
unsigned int alarm(unsigned int seconds);
(2)、alarm()函数在fork()函数调用之前,在子进程中有效;alarm()函数在fork()函数调用之后,在子进程中失效;
(3)、alarm()使用的基本方法:
先调用alarm()设置报警时钟,然后进程做某一项工作。如果进程在规定时间以内完成这一工作,就再调用alarm(0)时报警时钟失效。如果在规定时间内,未能完成这一项工作,系统内核就发出SIGALARM信号中断进程,执行信号处理函数。
#include <stdio.h>#include <signal.h>#define MAXTRIES 5#define LINESIZE 100#define TIMEOUT 5#define BELL '\007'#define TRUE 1#define FALSE 0static int time_out;static char inputline[LINESIZE];char *quickreply(char *prompt);int main(){printf("%s\n",quickreply("input"));return;}char *quickreply(char *prompt){int (*was)(),catch(),ntries;char *answer;was = signal(SIGALRM,catch); //设定捕捉SIGALRM的关联函数,并且保存原有关联for(ntries=0;ntries<MAXTRIES;ntries++){time_out = FALSE;printf("\n%S>",prompt);alarm(TIMEOUT); //设定定时器answer = gets(inputline);alarm(0); //关闭定时器if(!time_out) // 设定定时器后,信号一直在等待(超出定时器时间时,信号有内核发出,time_out变成true,继续下一次循环,最多循环5次),直到有输入后才退出(time_out在没有信号触发,一直是false)。{break;}}signal(SIGALRM,was);return(time_out?((char*)0):answer);}catch(){time_out = TRUE;putchar(BELL);}
pause()函数:
(1)、pause()函数在linux系统函数库unistd.h中的函数声明如下
int pause(void)
(2)、作用:系统调用pause()能够使调用进程暂停执行,直到接收到某种信号为止
#include <stdio.h>#include <signal.h>#define FALSE 0#define TRUE 1#define BELLS "\007\007\007"int alarm_flag = FALSE;setflag(){alarm_flag = TRUE;}int main(){int nsecs;int i;if((nsecs = atoi(argv[1]*60) <= 0){fprintf(stderr,"Invalid time\n");exit(2);}signal(SIGALRM,setflag); //设定信号关联动作alarm(nsecs); //设定定时器pause(); //调用等待信号if(alarm_flag){printf(BELLS);for(i=2;i<argc;i++){printf("%s\n",argv[i]);}}exit(0);}
10、信号和系统调用 setjmp() 和 longjmp() 函数之间的关系
(1)、在linux系统库函数中 setjmp.h 中调用
int setjmp(jmp_buf env);
void longjmp(jmp_buf env,int val);
(2)、 setjmp()函数-->能够保存程序中的当前位置(是通过保存堆栈环境实现的)
longjmp()函数-->能够把控制转回到被保存的位置
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
jmp_buf position;
int main()
{
int goback();
.....
.....
setjmp(position); //保存当前的堆栈环境
signal(SIGINT,goback);
domenu();
.....
.....
}
int goback()
{
fprintf(stderr,"\nInterrupted\n");
longjmp(position,1); //跳转回被保存的断点
}
- 进程间通信-信号
- 进程间通信--信号
- 进程间通信--信号
- 进程间通信-信号
- 进程间通信---信号
- 【进程间通信】信号
- 进程间通信----信号
- 【进程间通信】信号
- 信号通信(进程间通信)
- 进程间通信之信号
- 进程间通信:信号机制
- 3、进程间通信-信号
- 进程间通信--信号发送
- 父子进程间信号通信
- Linux 进程间通信 信号
- 嵌入式 进程间通信--信号
- 进程间通信______信号
- 进程间通信_04信号
- 飞思卡尔_第四章_存储保护单元
- virsh kvm xen xm qemu libvirt virt-manager概述
- java正则表达式构造子列表
- Linux top 命令
- 红帽 SOA 全面指导
- 进程间通信--信号
- DB2 通用数据库中的事务性日志记录概述
- linux中getopt的用法
- js显示当前日期和周时间
- 2013年大数据预测-IDC年度预测报告
- au手机,name属性相同的text标签,value覆盖的问题
- 洞察 DB2 Universal Database: SQL 语句的生命周期
- 高负载高并发网站架构分析
- 关于NP,NP-hard,P,NPC等相关问题的讨论【转帖】