Linux_信号

一.信号的概念和特点

    1.信号:Linux系统响应某些状况而产生的事件。进程在接收信号后会采取相应的动作

    2.查看所有信号的命令:kill -l

    3.信号常见的处理方式:

          a)忽略此信号

          b)执行该信号的默认处理动作

          c)提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉一个信号。

          d)SIGKILL SIGSTOP不能被忽略也不能被捕获

4.几种常见的信号 

2)ctrl+c  3)ctrl + / 4)非法指令 5)陷入内核 6)abort自杀  7)地址间映射  8)浮点数溢出  9)杀死进程  11)段错误

13)管道破裂   14)闹钟  15)缺省  17)子进程变成僵尸进程  18)继续  19)暂停   23)紧急数据  29)异步IO

二.产生信号的几种方式

  1.通过终端按键产生信号(像ctrl+c...)

      Core Dump(当一个进程异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core)

          默认不允许产生core文件,因为文件中可能包含用户密码等,不安全。

  2.硬件异常:CPU--除数0  SIGALFPE信号    ; mmu--内幕才能访问错误(段错误)--11号信号

  3.调用系统函数向进程发信号  kill raise absort(assert)

  4.软件条件 向读端都关闭了的管道中写数据,就会触发13号信号

三.API(系统函数)

  1.向指定的进程发送指定的信号

        a)头文件:#include <sys/types.h>
                              #include <signal.h>

        b)函数原型:int kill(pid_t pid, int sig);

        c)参数:pid>0给指定的pid进程发送信号   pid==0给本进程组的所有进程法信号  pid=-1给有权发送的所有进程法信号

                          pid<-1 给|pid|进程组的任何一个进程法信号

                          sig:发送哪一条信号

       d)返回值:成功返回0,失败返回-1

2.给本进程发信号

      a)头文件:#include <signal.h>

      b)函数原型:int raise(int sig);

      c)参数:sig发送哪一个信号

      d)返回值:成功返回0,失败返回-1

3.给进程组发送信号(进程组id就是第一个进程id)

      a)头文件:#include <signal.h>

      b)函数原型:int killpg(int pgrp, int sig);

      c)参数:pgrp:指定进程组的id  sig:几号信号

      d)返回值:成功返回0,失败返回-1

      e)进程组:管道连接的多个进程属于同一进程组。fork出来的子进程也属于同一进程组。

4.接收到信号终止进程

     a)头文件:#include <stdlib.h>

     b)函数原型:void abort(void);

     c)返回值:像exit函数一样,总是会执行成功,没有返回值

四.API(软件条件)

1.设定闹钟

      a)头文件:#include <unistd.h>

      b)函数原型:unsigned int alarm(unsigned int seconds);

      c)函数参数:seconds以秒为单位  ,若值为0,取消设置的闹钟。

      d)返回值:正确返回闹钟到期之前的秒数 ,没有闹钟返回0

      e)目的:调用alarm函数设置一个闹钟,告诉内核在seconds秒之后给当前进程发送一个SIGALRM信号,该信号的默认动作                           是终止当前进程。

      f)例子:(在2秒内能打印出多少数字)

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 
  4 int main(void){
  5     int i=0;
  6     alarm(2);
  7     for(i=0;;i++){                                          
  8         printf("%d\n",i);
  9     }
 10     return 0;
 11 
 12 }

设置一个定时器(setitimer)

  1 #include <stdio.h>
  2 #include <sys/time.h>
  3 #include <signal.h>
  4 #include <unistd.h>
  5 #include <string.h>
  6 
  7 void print(){
  8     printf("hello world\n");
  9 }
 10 
 11 
 12 int main(void){
 13     signal(SIGALRM,print);
 14     struct itimerval timer;
 15     //每隔1秒钟发送一个SIGALRM信号
 16     timer.it_interval.tv_sec=1;
 17     timer.it_interval.tv_usec=0;
 18     //3秒0微秒后开始启动定时器
 19     timer.it_value.tv_sec=3;//秒
 20     timer.it_value.tv_usec=0;//微秒
 21     
 22     int set=setitimer(ITIMER_REAL,&timer,NULL);
 23     if(set==-1){
 24         perror("setitimer");
 25         return 1;
 26     }
 27     printf("begin wait for 3 seconds\n");
 28     while(1);
 29 
 30 
 31     return 0;
 32 }                  

五.阻塞信号

    1)信号相关的概念

         a)信号递达:实际执行信号的处理动作

         b)信号未决:信号从产生到递达之间的状态

         c)进程可以选择阻塞某个信号

         d)被阻塞的信号将保持未决状态,直到解除阻塞,才能执行递达的动作

         e)阻塞和忽略是不同的,只要被阻塞就不会递达,而忽略是在递达之后可选的一种处理方式

   2)信号在内核中的表示

         每个信号都有两个标志位表示阻塞和未决,还有一个函数指针表示处理的动作,信号产生时会设置未决标志,直到递达清除该标志。常规信号在递达之前虽产生多次但只记一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

六.信号集操作函数(每一个bit代表一个信号,1有效,0无效)

     1)头文件:#include <signal.h>

     2)将信号集中的信号清空:int sigemptyset(sigset_t *set);

     3)初始化set所指向的信号集:int sigfillset(sigset_t *set);

     4)向信号集中添加信号:int   sigaddset(sigset_t   *set,   int signum);

     6)从信号集中删除信号:int   sigdelset(sigset_t   *set,   int signum);

     7)判断某个信号是否在信号集中:int sigismember(const sigset_t *set, int signum);   

     8)注意:在调用之前一定要将sigset_t的变量清空或者初始化

七.读取或更改进程的信号屏蔽字(sigprocmask)

     1)头文件: #include <signal.h>

     2)函数原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

     3)参数: oldset是非空指针,读取进程的当前屏蔽字通过oldset传出

                        set非空,根据how修改当前的信号屏蔽字

                        set和oldset都非空,将原来的信号屏蔽字备份到oldset中在根据how和set修改

     4)返回值:成功返回0,失败返回-1

     5)how的三种情况:SIG_BLOCK  set中包含希望添加到当前信号的信号屏蔽字   当前信号|set

                                          SIG_UNBLOCK  set中包含希望从当前信号的信号屏蔽字接触的信号   当前信号&~set

                                          SIG_SETMASK  设置当前的信号屏蔽字为set指向的值    当前信号=set

    6)如果函数解除了多个信号的阻塞,则他在返回前至少要将一个信号递达

八.读取当前的未决信号集(sigpending)

    1)头文件:#include <signal.h>

    2)函数原型:int sigpending(sigset_t *set);

    3)参数:读取当前的未决信号集,通过set传出

    4)返回值:成功返回0,失败返回-1

例子:(屏蔽了SIGINT信号)

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <signal.h>
  4 
  5 //判断当前的未决信号集
  6 void print(const sigset_t set){
  7     int i=0;
  8     for(;i<32;i++){
  9         if(sigismember(&set,i)==1)
 10             printf("1");
 11         else
 12             printf("0");
 13     }
 14     printf("\n");
 15     return;
 16 }
 17 
 18 int main(void){
 19     sigset_t t;
 20     sigemptyset(&t);
 21     sigaddset(&t,SIGINT);
 22     sigprocmask(SIG_BLOCK,&t,NULL);
 23 
 24     while(1){
 25         sigpending(&t);
 26         print(t);
 27         sleep(2);
 28     }
 29     return 0;
 30 }                                

运行结果:

九.信号的捕捉

    1)信号递达时调用用户自定义函数,这就叫捕捉信号。用户自定义函数和主函数是两个独立的流程,互不影响,没有调用和被调用的关系,使用不同的堆栈空间。

    2)sigaction(读取和修改与指定信号相关联的处理动作)

          a)头文件:#include <signal.h>

          b)函数原型:int sigaction(int signum, const struct sigaction *act,  struct sigaction *oldact);

          c)函数参数:signum 指定的信号编号   ,act非空 根据act修改该信号的处理动作,oldact非空,通过oact传出该信号原来                                       的处理动作

         d)返回值:成功返回0,失败返回-1

   3)pause(使调用进程挂起直到有信号递达)(只有出错的返回值)

实现一把mysleep

  1 #include <stdio.h>
  2 #include <signal.h>
  3 #include <unistd.h>
  4 
  5 void sig_alrm(){
  6 }
  7 
  8 void mysleep(int seconds){
  9     struct sigaction new,old;
 10     new.sa_handler=sig_alrm; //ALRM信号处理函数
 11     sigemptyset(&new.sa_mask);  //清空信号集
 12     new.sa_flags=0;
 13 
 14     sigaction(SIGALRM,&new,&old); //SIGALRM信号触发执行new的处理动作
 15     alarm(seconds);
 16     pause();//使进程挂起直到有信号递达
 17     alarm(0); //清除闹钟
 18     sigaction(SIGALRM,&old,NULL); //根据old修改动作,就是不做动作
 19 }
 20 
 21 int main(void){
 22     
 23     printf("I'm going to sleep!\n");
 24     mysleep(3);
 25     printf("I'm awake!\n");
 26 
 27 }                                 

十.不可重入条件(在执行过程中不能被中断)

   1.函数内部调用了malloc或free

   2.调用了标准IO

   3.使用了静态变量

 可重入函数:多个执行流调用同一个函数时没有逻辑错误

 竞态条件:由于进程/线程的切换造成了程序逻辑上的错误

 volatile:防止编译器在多执行流的情况下对代码过度优化

十二.SIGCHLD(处理僵尸进程)

  通过捕捉这个信号处理僵尸进程,信号捕捉函数中调用waitpid

十一.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值