Linux 信号总结以及常见信号使用

【摘要】信号是学习linux时必须要熟悉的一部分,没有了信号Linux系统就没办法正常的工作。而我们作为Linux程序员,也要用到信号来进行程序的运行,没有了信号,我们的工作将会变得一团糟。这几天,我把刚刚学的关于信号的知识点总结了一下,分享给大家,希望可以对大家有所帮助。

信号的概念


信号是linux系统为了响应某些状况而产生的事件。进程收到信号后应该采取相应的动作
哪些情况会引发信号

1.键盘事件
  ctrl +c  ctrl +\
2.非法内存  如果内存管理出错,系统就会发送一个信号进行处理
3.硬件故障  同样的,硬件出现故障系统也会产生一个信号
4.环境切换  比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统
怎样查看信号呢?

【 kill -l 】
这个命令就可以查看所有的信号啦,现在信号已经增加到65个了,但是在这里我要提一下,从33-64这些信号一般不会采用,这是为了区分可靠信号和不可靠信号而新增加的32个信号。关于可靠信号和不可靠信号,我会在下面加以说明滴。

下面我把最常用到的信号给大家解释一下
 
  (2) SIGINT   ctrl +c  终止信号
  (3)                ctrl  +\  暂停信号,放入后台 
  (4)                非法指令
(5)            abort   进程异常终止
  (7) SIGBUS  (虚实关系建立)       总线错误(从写的位置到物理内存,操作系统没有将磁盘的开始位置到物理内存之间建立      联系   mmap(把虚拟内存和磁盘文件的关系映射起来,如果磁盘大小大于0,就建立这种关系
  (9) SIGKILL  kill - 9 pid  杀死进程
  (11) SIGSEGV 段错误
(13) 管道破裂
(14)闹钟
(15) 缺省终止某个进程,终止掉
(17)子进程死的时候会给父进程发送这个信号
(19)进程暂停
(23) SIGURG 紧急数据
(29) 异步 IO

信号的默认处理方式:
这个就要使用  man 手册自己去查找啦,有好多呢,记住常用的就可以啦 
man 7 signal  在第七页,所以直接用 man 7

进程收到信号的三种处理方式

默认:如果是系统默认的话,那就会终止这个进程
忽略 :信号来了我们不处理,装作没看到   SIGKILL  SIGSTOP 不能忽略
捕获并处理 :当信号来了,执行我们自己写的代码(捕获信号这个动作是需要我们完成的)  SIGKILL SIGSTOP 不能捕获

注册信号
       typedef void  (* sighandler_t) (int);
       sighandler_t signal (int signum   //要注册的信号
,sighandler_t handler);    //信号执行函数
1.自己定义函数
2.SIG_IGN  #define SIG_IGN  (sighandler_t(1))把1强制转化为函数指针类型
3.SIG_DFL  #define SIG_IGN  (sighandler_t(0))把0强制转化为函数指针类型
      
返回错误 SIG_ERR
#define SIG_ERR
信号是异步事件,当信号到达时,保存当前的执行环境,转去执行信号处理函数,当信号处理函数完毕,恢复现场,继续执行

这里我们看一下可靠信号和不可靠信号的区别吧
不可靠信号
Linux的信号继承自早期的Unix信号,Unix信号的缺陷
1.信号处理函数执行完毕,信号恢复成默认处理方式(Linux已经改进)
2.会出现信号丢失,信号不排队
1-31  都是不可靠的,会出现信号丢失现象
可靠信号  
34-64重新设计的一套信号集合
不会出现信号丢失,支持排队,信号处理函数执行完毕,不会恢复成缺省处理方式
实时信号 : 就是可靠信号
非实时信号:不可靠信号

发送信号:
 kill  -信号值 pid
int kill(int pid,int signum)
   pid > 0 :发送给pid进程
   pid = 0 :调用者所在进程组的任一进程
   pid = -1:  有权发送的任何一个进程,除了1
   pid < -1 |pid|进程组所有的进程

进程组:进程组中有若干个进程
用管道连接的进程, fork创建的父子进程都属于同一个进程组
sleep 返回值 > 0 表示还剩多少秒没睡就被信号打断

给自己发信号
raise(  int signum  )
kill(getpid() ,signum)

给进程组发信号
int killpg(int gid,int signum);
暂停进程,直到被信号打断
int  pause( void )把当前进程变成就绪态,让出CPU  calling process
SIGALRM
int alarm(int sec)
当sec规定的时间到了,触发SIGALRM信号
如果sec是0,表示清除信号

下面是两个SIGALRM信号的使用范例,希望通过这两个实例可以加深你们对于闹钟信号的理解啊

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>

void handler(int s) //信号到来,则执行这个函数,输出超时
{
  printf("超时\n");
  exit(1);
}

int main()
{
    char buf[100]={};
    printf("输入名字");
    
   signal(SIGALRM,handler); //定义一个信号函数,当SIGALRM信号发过来时,执行handler函数
    alarm(5);  //设置五秒的时钟,五秒内如果没有执行输入操作就会发送信号
      
    scanf("%s",buf);
    alarm(0);  //如果五秒内执行了操作,那就清空闹钟
    
    printf("名字为:%s\n",buf);
    
    for(; ;) //验证闹钟时间已经清空
    {
      fflush(stdout); 
      printf(".");
      sleep(1);
    }
    
    }
执行结果
基于闹钟原理,我们再来实现一个更加复杂的小型考试系统
代码如下

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>

int count=0;   //初始化
int wrong=0;   //初始化

void handler(int s)  //当闹钟信号发过来,执行这个函数,输出对错的个数
{
  printf("time out \n");
  printf("right = %d,wrong = %d\n ",count,wrong);
  exit(1);
}

int main()
{
  int i = 0;
  signal(SIGALRM,handler); //定义信号函数接收信号
  
 alarm(30);  //设置闹钟时间为30秒
 srand(getpid());  //用子进程id作为随机种子数
 for(i=0;  i<10;i++)  //循环十次,输出随机数相加结果
{
   int left = rand() %10;
   int right = rand() %10;
   printf("%d + %d=",left,right); 
   int ret;

   while (getchar()!='\n'); //清空输入缓冲区,防止异常输入使得程序崩溃
   scanf("%d",&ret);
   if(left +right == ret) //判断对错
   {
     count++;
   }
   else
   {
     wrong++;
   }
}   
printf("做完了\n");
printf("right =%d,wrong = %d \n",count,wrong);
}

程序结果如下
从这两个实例,我们已经可以知道时钟的简单用法啦,那么说了这么多,我觉得你们也可以自己上手去做一个以闹钟为中心的小程序啦。再另一篇博客里面我会讲一讲时钟的用法,希望可以帮助到你们哦。
注:我也是个初学者啦,如果哪里又纰漏之处,欢迎指正啊,也可以私信我,我很喜欢交流的。


猜你喜欢

转载自blog.csdn.net/zb1593496558/article/details/80280346