欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

信号

最编程 2024-04-13 22:47:48
...

我们使用过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的时候子进程就会暂停:

信号是对终端机的一种模拟,也是一种异步通信方式。