信号的基本概念
每个信号都有一个编号和一个宏定义名称 ,这些宏定义可以在 signal.h 中找到。
使用kill -l命令查看系统中定义的信号列表: 1-31是普通信号; 34-64是实时信号
所有的信号都由操作系统来发!
对信号的三种处理方式忽略此信号:大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。这两种信号不能被忽略的,原因是:它们向超级用户提供一种使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是示定义的。
直接执行进程对于该信号的默认动作 :对大多数信号的系统默认动作是终止该进程。
捕捉信号:执行自定义动作(使用signal函数),为了做到这一点要通知内核在某种信号发生时,调用一个用户函数handler。在用户函数中,可执行用户希望对这种事件进行的处理。注意,不能捕捉SIGKILL和SIGSTOP信号。
1 2 3
#include <signal.h> typedef void( *sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
signal函数的作用:给某一个进程的某一个特定信号(标号为signum)注册一个相应的处理函数,即对该信号的默认处理动作进行修改,修改为handler函数所指向的方式。
第一个参数是信号的标号
第二个参数,sighandler_t是一个typedef来的,原型是void (*)(int)函数指针,int的参数会被设置成signum
举个代码例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include<stdio.h> #include<signal.h> void handler(int sig) { printf("get a sig,num is %d\n",sig); } int main() { signal(2,handler); while(1) { sleep(1); printf("hello\n"); } return 0; }
修改了2号信号(Ctrl-c)的默认处理动作为handler函数的内容,则当该程序在前台运行时,键入Ctrl-c后不会执行它的默认处理动作(终止该进程)
信号的处理过程:
进程收到一个信号后不会被立即处理,而是在恰当 时机进行处理!什么是适当的时候呢?比如说中断返回的时候,或者内核态返回用户态的时候(这个情况出现的比较多)。
信号不一定会被立即处理,操作系统不会为了处理一个信号而把当前正在运行的进程挂起(切换进程),挂起(进程切换)的话消耗太大了,如果不是紧急信号,是不会立即处理的。操作系统多选择在内核态切换回用户态的时候处理信号,这样就利用两者的切换来处理了(不用单独进行进程切换以免浪费时间)。
总归是不能避免的,因为很有可能在睡眠的进程就接收到信号,操作系统肯定不愿意切换当前正在运行的进程,于是就得把信号储存在进程唯一的PCB(task_struct)当中。
产生信号的条件1.用户在终端按下某些键时,终端驱动程序会发送信号给前台程序。
例如:Ctrl-c产生SIGINT信号,Ctrl-\产生SIGQUIT信号,Ctrl-z产生SIGTSTP信号
2.硬件异常产生信号。
这类信号由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。
例如:当前进程执行除以0的指令,CPU的运算单元会产生异常,内核将这个进程解释为SIGFPE信号发送给当前进程。
当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
3.一个进程调用kill(2)函数可以发送信号给另一个进程。