Linux信号

进程信号

基本认识:信号是进程之间事件异步通知的一种方式, 属于软中断

信号种类

kill -l 命令查看linux信号(需在root用户下才能看到全部信号)

共62种信号, 分为非可靠信号和可靠信号

  • 非可靠信号为 1~32
  • 可靠信号为 34 ~ 64
信号的生命周期

信号产生 —》信号再进程中注册 —》信号再进程中注销 —》信号处理

信号产生

硬件:ctrl + c ctrl + | ctrl+z

软件: kill -signo pid 命令 kill() raise() abort() alarm() 程序异常

core dumped --核心转储 程序异常退出时保存程序运行信息 方便事后调试

core dumped --默认关闭 因为其占磁盘资源, 安全性考虑

ulimit -a 查看core dump开启状态

ulimit -c 1024 设置核心转储文件大小, 开启核心转储

调试:gdb ./loop —> core-file core.pid —> bt

#include <stdio.h>    
#include <signal.h>    
#include <unistd.h>    
#include <stdlib.h>    
    
int main(){    
  sleep(2);    
  int kill(pid_t pid, int sig);    
  //给指定进程发送指定信号    
  //产生段错误    
  kill(getpid(), SIGSEGV);    
      
  int raise(int sig);    
  //给调用进程发送指定信号                                                                                                                       
  raise(SIGQUIT);    
      
  void abort(void);    
  //给调用进程发送SIGABRT    
  //异常终止一个进程    
  abort();
    
  //unsigned int alarm(unsigned int seconds);
  //seconds秒之后给调用进程发送SIGALRM信号
  //seconds==0表示取消定时器
  //返回值上一个定时器剩余的时间或0
  int ret = alarm(3);
  sleep(4);
  return 0;
}
信号在进程中的注册
  • 非可靠信号注册
    判断pending信号集合位图相应位是否为1;若为0, 为信号阻止sigqueue节点, 添加到链表中,并且pending位图置1(信号已经注册过还没有被处理), 则什么都不做(等于丢掉)

  • 可靠信号注册

    不管位图是否为1,阻止节点,添加到链表中,并且位图置1(信号不会被丢掉)

信号的注销
  • 非可靠信号注销

    因为非可靠信号的信号节点只有一个,因此删除节点,位图直接置0

  • 可靠信号

    因为可靠信号的信号节点有可能会有多个,若还有相同信号节点,则位图依然置1,否则置0

信号的处理
  • 默认处理方式 SIG_DFL
  • 忽略处理方式 SIG_IGN
  • 自定义处理方式 void (*sighandler_t)(int)

在这里插入图片描述

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

// 自定义信号
void handler() {
    printf("--------\n");
}
int main() {
 // ctrl + c 触发信号
 signal(SIGINT, handler);
    while(1) {
        printf("hhhhhh\n");
        sleep(5);
    }
 return 0;
}

在这里插入图片描述

  • 自定义信号及信号捕捉实例
#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
#include <signal.h>    

// act 为新信号  oldact保存老信号
struct sigaction act, oldact;    
        
void sigcb(int signo){    
  printf("recv signo:%d\n", signo);    
  sigaction(signo, &oldact, NULL);    
}    
    
int main(){
  // 为SIGINT信号自定义处理方式
  act.sa_handler = sigcb;    
  act.sa_flags = 0;
  // 清空信号集合set
  sigemptyset(&act.sa_mask);
  //int sigaction(int signum,struct sigaction *act,struct sigaction *oldact);
  //使用act动作替换signum原有的处理动作,并且将原有处理动作拷贝到oldact中
  sigaction(SIGINT, &act, &oldact);    
  while(1){    
    printf("-------\n");    
    sleep(1);    
  }                                                                                                                                              
  return 0;    
}
// 该实例是通过oldact保存原有SIGINT信号处理方式, 通过一次替换后, 还原原始信号操作
信号的阻塞

暂时阻止信号被递达 信号依然可以注册, 但是只是暂时不处理,解除阻塞之后才会处理

信号的递达:动作 -> 信号的处理

信号的未决:状态 -> 信号从产生到处理之间所处的状态

在这里插入图片描述

阻塞原理:在PCB的blocked 信号阻塞集合中标记暂时不处理信号(将blocked位图集合中对应的位 置1, 表示阻塞这个信号)

注意: 有两个信号SIGKILL 9 SIGSTOP 19无法被阻塞, 无法自定义,无法被忽略

API功能
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);阻塞解除阻塞信号
int sigemptyset(sigset_t *set);清空信号集合
int sigfillset(sigset_t *set);向集合添加所有信号
int sigaddset (sigset_t *set, int signo);向集合添加指定信号
int sigdelset(sigset_t *set, int signo);从集合中移除执行信号
int sigismember(const sigset_t *set, int signo);判断信号是否在集合中
int sigpending(sigset_t *set);获取未决信号
  • 信号阻塞实例(实现sleep函数)

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
    void sigcb2(int signo)
    {
        sleep(10);
    }
    void sigcb(int signo)
    {
    }
    int mysleep(int nsec)
    {
        signal(SIGALRM, sigcb);
        sigset_t set, old;
        sigemptyset(&set);
        sigprocmask(SIG_BLOCK, &set, &old);
        alarm(nsec);
    
        sigprocmask(SIG_UNBLOCK, &set, &old);
        //int sigsuspend(const sigset_t *mask);
        //临时使用mask替换阻塞集合,陷入休眠,被唤醒之后阻塞集合还原
        sigset_t mask;
        sigfillset(&mask);
        sigdelset(&mask, SIGALRM);//除了SIGALRM所有信号
        //这时候除了SIGALRM,所有信号都被阻塞,陷入休眠,只有SIGALRM能够唤醒这个休眠
        sigsuspend(&mask);
        sigprocmask(SIG_SETMASK, &old, NULL);
        signal(SIGALRM, SIG_DFL);
    }
    
    int main()
    {
        signal(SIGQUIT, sigcb2);
        mysleep(3);
        printf("------\n");
        return 0;
    }
    
  • volatile作用

    保持内存的可见性,告知编译器,被该关键字修饰的变量, 不允许被优化, 对该变量的任何操作,都必须在真实的内存中进行操作

  • SIGCHLD信号的妙用

    在之前我们使用轮询的方式等待子进程结束后清理, 现在由于我们知道进程终止和退出会发出一个SIGCHLD信号, 该信号的处理方式是默认忽略,所以我们使用自定义该信号处理方式来实现,自动对退出后的子进程的清理工作

     #include <stdio.h>    
      #include <unistd.h>    
      #include <signal.h>    
      #include <stdlib.h>    
      #include <sys/wait.h>    
          
      void handler(){    
        pid_t id;    
         while( (id = waitpid(-1, NULL, WNOHANG)) > 0 ){            
           printf("wait child success: %d\n", id);                                                                                                   
         }     
        printf("ok, child is quit %d\n", getpid());    
      }    
          
      int main(){    
        signal(SIGCHLD, handler);    
        pid_t pid;    
        pid = fork();    
        if (pid < 0){    
          return -1;    
        } else if (pid == 0){    
          printf("child: %d\n", getpid());    
          sleep(3);    
          exit(1);    
        }    
        while(1){    
          printf("parent can do anything!\n");    
          sleep(1);    
        }    
        return 0;    
      } 
    
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值