信号
我们使用过windows的都知道,当一个程序被卡死的时候不管怎样都没反应,这样我们就可以打开任务管理器直接强制性的结束这个进程,这个方法的实现就是和Linux上通过生成信号和捕获信号来实现相似的,运行过程中进程捕获到这些信号做出相应的操作使最终被终止。
信号的主要来源是分为两部分,一部分是硬件来源,一部分是软件来源;进程在实际中可以用三种方式来响应一个信号:一是忽略信号,不对信号做任何操作,其中有两个信号是不能别忽略的分别是SIGKILL和SIGSTOP。二是捕捉信号,定义信号处理函数,当信号来到时做出响应的处理。三是执行缺省操作,Linux对每种信号都规定了默认操作。注意,进程对实时信号的缺省反应是立即终止。
发送信号的函数有很多,主要使用的有:kill()、raise()、abort()、alarm()
。
先来熟悉下kill函数,进程可以通过kill()函数向包括它本身在内的其它进程发送一个信号,如果程序没有发送这个信号的权限,对kill函数的调用将会失败,失败的原因通常是由于目标进程由另一个用户所拥有。
kill函数的原型为:
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);
它的作用是把信号sig发送给进程号为pid的进程,成功时返回0。kill调用失败返回-1,调用失败通常有三大原因:
- 1、给定的信号无效
- 2、发送权限不够
- 3、目标进程不存在
还有一个非常重要的函数,信号处理signal函数。程序可以用signal函数来处理指定的信号,主要通过恢复和忽略默认行为来操作。signal函数原型如下:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
我们来看一个例程了解一下signal函数。signal.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//函数ouch对通过参数sig传递进来的信号作出响应。
void ouch(int sig)
{
printf("signal %dn", sig);
//恢复终端中断信号SIGINT的默认行为
(void) signal(SIGINT, SIG_DFL);
}
int main()
{
//改变终端中断信号SIGINT的默认行为,使之执行ouch函数
(void) signal(SIGINT, ouch);
while(1)
{
printf("Hello World!n");
sleep(1);
}
return 0;
}
运行结果:
可以看出当我按下ctrl+c的时候并不会退出,只有当再次按下ctrl+c的时候才会退出。造成的原因是因为SIGINT的默认行为被signal函数改变了,当进程接受到信号SIGINT时,它就去调用函数ouch去处理,注意ouch函数把信号SIGINT的处理方式改变成默认的方式,所以当你再按一次ctrl+c时,进程就像之前那样被终止了。
下面是几种常见的信号:
- SIGHUP :从终端上发出的结束信号
- SIGINT :来自键盘的中断信号 ( ctrl + c )
- SIGKILL :该信号结束接收信号的进程
- SIGTERM:kill 命令发出的信号
- SIGCHLD:标识子进程停止或结束的信号
- SIGSTOP:来自键盘 ( ctrl + z ) 或调试程序的停止执行信号。
信号发送主要函数有kill和raise。上面我们知道kill函数的用法也清楚kill函数是可以向自身发送信号和其它进程发送信号,raise与之不同的是只可以向本身发送信号。
通过raise函数向自身发送数据,使子进程暂停通过测试如下: raise.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int ret;
if((pid=fork())<0)
{
printf("Fork errorn");
exit(1);
}
//子进程
if(pid==0)
{
//在子进程中使用raise()函数发出SIGSTOP信号,使子进程暂停
printf("I am child pid:%d.I am waiting for any signaln",getpid());
raise(SIGSTOP);
printf("I am child pid:%d.I am killed by progress:%dn",getpid(),getppid());
exit(0);
}
//父进程
else
{
sleep(2);
//在父进程中收集子进程发出的信号,并调用kill()函数进行相应的操作
if((waitpid(pid,NULL,WNOHANG))==0)
{
//若pid指向的子进程没有退出,则返回0,且父进程不阻塞,继续执行下边的语句
if((ret=kill(pid,SIGKILL))==0)
{
printf("I am parent pid:%d.I am kill %dn",getpid(),pid);
}
}
//等待子进程退出,否则就一直阻塞
waitpid(pid,NULL,0);
exit(0);
}
}
当调用raise的时候子进程就会暂停:
信号是对终端机的一种模拟,也是一种异步通信方式。