信号通信
信号是软件中断。信号(signal)机制是Unix系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。
很多条件可以产生一个信号,当用户按某些终端键时,产生信号。在终端上按DELETE键通常产生中断信号(SIGINT)。这是停止一个已失去控制程序的方法。
硬件异常产生信号:除数为0、无效的存储访问等等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。
例如,对执行一个无效存储访问的进程产生一个SIGSEGV。
内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:
异常:进程运行过程中出现异常;
其它进程:一个进程可以向另一个或一组进程发送信号;
终端中断:Ctrl-C,Ctrl-\等;
作业控制:前台、后台进程的管理;
分配额:CPU超时或文件大小突破限制;
通知:通知进程某事件发生,如I/O就绪等;
报警:计时器到期。
命令:top 可以查看系统中的信号
下面是几个常见的信号。
SIGHUP: 从终端上发出的结束信号;
SIGINT: 来自键盘的中断信号(Ctrl-C);
SIGQUIT:来自键盘的退出信号(Ctrl-\);
SIGFPE: 浮点异常信号(例如浮点运算溢出);
SIGKILL:该信号结束接收信号的进程;
SIGALRM:进程的定时器到期时,发送该信号;
SIGTERM:kill 命令发出的信号;
SIGCHLD:标识子进程停止或结束的信号;
SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号
可以要求系统在某个信号出现时按照下列三种方式中的一种进行操作。
(1) 忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的原因是:它们向超级用户提供一种使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是未定义的。
(2) 捕捉信号。为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。如果捕捉到SIGCHLD信号,则表示子进程已经终止,所以此信号的捕捉函数可以调用waitpid以取得该子进程的进程ID以及它的终止状态。
(3) 执行系统默认动作。对大多数信号的系统默认动作是终止该进程。
signal函数:
#include <signal.h>
void (*signal (int signo, void (*func)(int)))(int)
返回:成功则为以前的信号处理配置,若出错则为SIG_ERR
func的值是: (a)常数SIG_IGN,或(b)常数SIG_DFL,或(c)当接到此信号后要调用的函数的地址。
1.如果指定SIG_IGN ,则向内核表示忽略此信号(有两个信号SIGKILL和SIGSTOP不能忽略)。
2.如果指定SIG_DFL,则表示接到此信号后的动作是系统默认动作。
3.当指定函数地址时,我们称此为捕捉此信号。我们称此函数为信号处理程序(signal handler)或信号捕捉函数(signal-catching function)。
信号通信实例:
#include <iostream> #include <signal.h> #include <cstdlib> #include <sys/wait.h> using namespace std; void sign(int num)//自定义信号处理函数{ if(SIGINT == num) { while(1) { cout<<"Ctrl+C键被按下!"<<endl; sleep(1); } } else if(SIGQUIT == num) { while(1) { cout<<"Ctrl+\被按下!"<<endl; sleep(1); } } //cout<<"除数不能为0!"<<endl; /* cout<<"Sorry !"<<endl; exit(0);*/ /* sleep(1); static int i=0; cout<<++i<<endl; */ // cout<<"Quit!"<<endl; /*cout<<"One child will die !"<<endl; int pid,status; pid = wait(&status);//等待子进程结束*/ } int main() { //什么都不写时,默认系统信号处理 if(SIG_ERR == signal(SIGCHLD,sign))//自定义对信号的处理 { cout<<"安装信号处理函数失败!"<<endl; } //signal(SIGINT,SIG_IGN);//SIG_IGN表示使当前进程忽略信号 /* signal(SIGFPE,sign); int i = 0; cout<< 5/i <<endl;//cpu不断运算 */ /* signal(SIGSEGV,sign); cout<<atoi(NULL);*/ //对于同种信号,先来先处理 //对于不同信号,最后来的优先处理 signal(SIGINT,sign);//不可靠信号可能会丢失 //signal(SIGQUIT,sign); // cout<<*((int*)1)<<endl; while(1); /* int cid = fork(); if(cid<0) { cout<<"fork called failed !"<<endl; } else if(cid == 0) { sleep(1); } else { while(1); } */ return 0; }
运行该程序后,如果按下Ctrl+C,系统收到的信号后将按自定处理方式输出“Ctrl+C键被按下”。