8.信号 C++/Linux-2022-10-17

信号

  • 信号机制:进程B发给进程A,内核产生信号,内核处理

  • 信号的产生:

    • 按键产生:CTRL+c,CTRL+z。。。
    • 调用函数:kill。。。。
    • 定时器:alarm
    • 命令:kill
    • 硬件异常;段错误,浮点型错误,总线错误
  • 信号的状态:

    • 产生
    • 递达 信号到达且处理完
    • 未决 信号阻塞
  • 信号的默认处理方式

    • 忽略
    • 执行默认动作
    • 捕获
  • 信号的四要素

    • 编号
    • 事件
    • 名称
    • 默认处理动作:
      • 忽略
      • 终止
      • 终止+core
      • 暂停
      • 继续
  • 系统api:kill

    • 头文件:#include <sys/types> #include <signal.h>
    • 函数:kill(pid_t pid, sig)
      • pid是要kill的进程
      • sig传入的信号,SIGKILL就是杀死进程
  • 系统api:raise

    • 头文件:#include <signal.h>
    • 函数: void raise(sig),对自己操作,相当于kill(getpid(),sig)
  • 系统api:abort

    • 头文件:#include <stdlib.h>
    • 函数:void abort();,异常退出
  • 系统api:alarm

    • 头文件:#include <stdlib.h>
    • 函数:unsigned int alram(unsigned int seconds);
    • 在seconds后,内核会给进程发送14)SIGALRM信号。进程收到该信号,默认动作终止
    • 返回0或剩余的秒数,五失败
    • 如果传入参数为0,代表取消闹钟
    • 一个进程只有一个alarm
  • 系统api:setitimer

  • 头文件:#include <sys/time.h>

  • 函数:int setitimer(int which, const struct itimerval *new_value, const struct itimerval *old_value);

    • which:

      • ITIMER_REAL : 对应信号SIGALRM,(常用),自然定时时间
      • ITIMER_VIRTUAL : 对应信号SIGVTALRM,计算进程执行时间
      • ITIMER_PROF : 对应信号是SIGPROF,进程执行时间+调度时间
    • new_value :

    • old_value :

    • 结构体

      • struct itimerval{
        	struct timerval it_interval;//周期时间设置
        	struct timerval it_value;//下次的闹钟时间
        }
        struct timeval{
        	time_t tv_sec ;/秒
        	suseconds_t tv_usec; //纳秒
        }
        
  • 例子

  • #include <stdio.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <signal.h>
    
    //捕获信号
    void catch_sig(int num){
        printf("cat %d sig",num);
    }
    
    int main()
    {
        signal(SIGALRM,catch_sig);//先设定信号
        struct itmerval myit = {{3,0},{5,0}};//第一个周期是5秒,后面周期是3s
        settimer(ITIMER_REAL,&myit,NULL);
        
        while(1){
            printf("kill me!\n");
            sleep(1);
        }
    }
    
  • 信号捕捉:防止进程意外死亡

    • typedef void(*sighandle_t)(int) e.g. void catch_sig(int num)

    • 注册捕捉函数: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

      • signum 捕捉的信号

      • act 传入的动作

      • oldact 原动作,恢复现场

      • struct sigaction{
        	void (*sa_handler)(int);//函数指针
        	void (*sa_sigaction)(int, sigaction_t *,void *);
        	sigset_t sa_mask;//在执行捕捉函数期间,临时屏蔽的信号集
        	int sa_flags;//一般填0
        	void (*sa_restorer)(void);//无效
        }
        
      • 往sa_mask添加临时屏蔽信号:sigaddset(&sa_mask,SIGQUIT);只生效一次

        e.g.

        #include <stdio.h>
        #include <signal.h>
        #include <unistd.h>
        #include <sys/time.h>
        
        void catch_sig(int num)
        {
            printf("catch %d sig\n",num);
        }
        
        int main()
        {
            //注册捕捉函数
            struct sigaction act;
            act.sa_flags = 0;
            act.sa_handler = catch_sig;
            
            sigemptyset(&act,sa_,mask);//清空
            sigaction(SIGALRM,&act,NULL);
            
            //setitimer
            strucr setitimer myit = {{3,0},{5,0}};
            setitimer(ITIMER_REAL,&myit,NULL);
            while(1){
                printf("kill me\n");
                sleep(1);
            }
            
            return 0;
        }
        
    • 捕捉特性

      • 某信号捕捉函数执行期间,某信号自动被屏蔽
      • 阻塞的常规信号不支持排队,产生多次只记录一次
  • SIGCHLD

    • 子进程在暂停或者退出的时候会发送SIGCHLD信号,可以通过捕捉SIGCHLD来回收子进程

    • #include <stdio.h>
      #include <unistd.h>
      #inclued <sys/types.h>
      #include <signal.h>
      
      void catch_sig(int num){
      	pid_t wpid;
      	while((wpid = waitpid(-1,NULL,WNOHANG))>0){
              printf("child %d ok\n",wpid);
          }
      }
      
      int main()
      {
          int i = 0;
          pid_t pid;
          for(int i 0;i<10;i++){
              pid = fork();
              if(pid == 0){
                  break;
               } 
          }
          
          //在子进程死之前屏蔽信号
          sigset_t myset,oldset;
          sigemptyset(&myset);
          sigaddset(&myset,SIGCHLD);//添加屏蔽信号集
          //oldset,保留现场
          sigprocmask(SIG_BLOCK,&myset,&oldset);
          
          if(i == 10){
              struct sigaction act;
              act.sa_flags = 0;
              sigemptyset($act.sa_mask);
              act.sa_handler = catch_sig;
              
              sifaction(SIGCHLD,&act,NULL);//注册捕捉函数
          
              //解除屏蔽信号
              sigprocmask(SIG_SETMASK,&oldset,NULL);
              while(1){
                  sleep(1);
              }
          }else if(i<10){
              printf("iam child,pid=%d",getpid());
          }
      }
      
    • 为了避免子进程死在注册捕捉函数前成为僵尸进程,可以先屏蔽/忽略 SIGCHLD信号

守护进程
  • 创建会话
    • 不能是进程组长
    • 建立会话时,父进程退出,子进程setsid : pid_t getdid(pid_t pid);成功返回进程会话的pid,失败-1,设置errno
    • 守护进程一般以的结尾:daemon
  • 创建守护进程模型
    • 创建子进程,父进程退出
    • 在子进程中创建会话,setsid()
    • 改变当前目录,chdir()
    • 设置文件权限,umask()
    • 关闭文件描述符,0,1,2,为了避免浪费资源
    • 执行守护进程核心
    • 守护进程退出
  • shell端达到守护进程:nohup ./a.out >> &
    • nohup指令会让cmd接收不到SIGHUP信号
    • &代表后台运行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值