进程信号(牛客版)

信号概述

在C++中,信号集(signal set)是一组信号的集合,常规信号(standard signal)是指一些由操作系统定义的常见信号。这些常规信号具有预定的含义和默认的处理方式。

常见的常规信号包括:

  1. SIGINT(中断信号):由终端键盘输入产生的中断信号,通常是通过按下 Ctrl+C 触发。
  2. SIGQUIT(退出信号):由终端键盘输入产生的退出信号,通常是通过按下 Ctrl+\ 触发。
  3. SIGILL(非法指令信号):表示进程执行了一个非法的、无效的指令。
  4. SIGABRT(异常终止信号):表示进程调用了 abort() 函数,通常用于异常终止一个程序。
  5. SIGFPE(浮点异常信号):表示进程执行了一个浮点数运算错误,比如除以零。
  6. SIGSEGV(段错误信号):表示进程访问了一个无效的内存地址,通常是内存访问越界或者未初始化的指针导致的。
  7. SIGTERM(终止信号):表示进程收到了终止请求,通常是通过发送 kill 命令给进程或发送 kill() 函数调用触发。
  8. SIGCHLD(子进程状态改变信号):表示子进程状态发生了变化,通常用于通知父进程子进程的退出状态。
  9. SIGCONT(继续信号):表示继续一个之前被暂停的进程。
  10. SIGSTOP(停止信号):表示停止一个进程的执行。

这些常规信号由操作系统在不同情况下生成和传递给进程,可以通过注册信号处理函数来捕捉和处理这些信号。例如,可以使用 signal() 函数或 sigaction() 函数来注册信号处理函数,以便在接收到指定信号时执行自定义的操作或进行特定的处理。

查看所有信号

kill -l

其中1~31号新号为常规信号

kill函数

功能:用于向指定进程或进程组发送信号

原型

#include <sys/types.h>
#include <signal.h>
​
int kill(pid_t pid, int sig);

参数

  • pid参数为要发送信号的进程ID

  • sig参数为要发送的信号编号。

常见的信号包括:

  • SIGKILL(编号 9):强制终止进程。

  • SIGTERM(编号 15):请求进程正常终止。

  • SIGUSR1(编号 10):用户自定义信号1。

  • SIGUSR2(编号 12):用户自定义信号2。

例如,向进程 ID 123 发送 SIGTERM 信号:

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
using namespace std;
​
/*
demo功能:
让子进程执行2两秒之后,由父进程中断子进程的执行
*/
void test()
{
pid_t pid=fork();
if(pid<0)
{
perror("fork error");
exit(-1);
}
​
if(pid==0)//子进程
{
for(int i=0;i<5;i++)
{
cout<<"child process..."<<endl;
sleep(1);
}
}
else if(pid>0)//父进程
{
cout<<"parent process..."<<endl;
sleep(2);
cout<<"kill child process now"<<endl;
kill(pid,SIGINT);//对其他进程发送信号,SIGINT信号为中断进程,也就是ctrl+c命令
}
}
​
int main()
{
test();
return 0;
}

alarm定时器

功能:一个定时器函数,用于在指定的时间后发送 SIGALRM(终止进程) 信号给调用进程

原型

#include <unistd.h>
​
unsigned int alarm(unsigned int seconds);

参数

  • 从调用 alarm()开始到触发定时器所需的秒数。当定时器触发时,进程会收到 SIGALRM 信号。

返回值

返回之前设置的剩余定时时间(如果有),或者0(如果之前没有设置定时器),或者剩余时间的估计(如果之前设置的时间小于新设置的时间)。

#include<iostream>
#include<unistd.h>
using namespace std;
​
/*
测试计算机1s可以数几个数
*/
void test()
{
    alarm(1);//非阻塞函数
    for(int i=1;;i++)
        cout<<i<<endl;
}
​
int main()
{
    test();
    return 0;
}
​

setitimer定时器

功能定时触发信号的发送,可以用来实现定时任务、性能统计、超时处理等功能。

原型

#include <sys/time.h>
​
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

参数

  • which:定时器类型,可以是以下三个值之一:

    • ITIMER_REAL - 真实时间定时器以系统真实的时间来计算,当计时到达时,将给进程发送 SIGALRM 信号。

    • ITIMER_VIRTUAL - 虚拟时间定时器只在当前进程执行时计时,当计时到达时,将给进程发送 SIGVTALRM 信号。

    • ITIMER_PROF - 进程执行和系统运行的时间都计时,当计时到达时,将给进程发送 SIGPROF 信号。

  • new_value:指向 itimerval 结构的指针,用于设置新的定时器值。

  • old_value:指向 itimerval 结构的指针,用于存储旧的定时器值

  • itimerval 结构定义如下:

struct itimerval {
    struct timeval it_interval; // 定时器重复计时间隔,第一次触发后每次被触发的时间间隔
    struct timeval it_value;    // 定时器初始计时值,从程序开始运行到定时器第一次触发的时间间隔
};
​
struct timeval {
    time_t      tv_sec;     // 秒数
    suseconds_t tv_usec;    // 微秒数
};

/*
demo功能:
创建一个定时器,当程序开始运行后3s触发,并且触发定时器后1秒给系统发送SIGALARM信号终止当前进程
*/
void test()
  {
      struct itimerval new_val;
      new_val.it_interval.tv_sec=3;
      new_val.it_interval.tv_usec=0;
​
      new_val.it_value.tv_sec=1;
      new_val.it_value.tv_usec=0;
​
      //非阻塞函数
      int ret=setitimer(ITIMER_REAL,&new_val,nullptr);
      cout<<"start"<<endl;
      if(ret==-1)
      {
          perror("error");
          exit(-1);
      }
      getchar();
  }

signal信号捕捉

功能:捕捉信号,并对捕捉到的信息设置捕捉行为

原型

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数

  • signum:要捕捉的信号

  • handler:捕捉到信号后如何处理

    • SIG_IGN:忽略新红

    • SIG_DFL:使用信号的默认行为

    • 使用回调函数sighandler_t:自定义捕捉行为

返回值

成功:返回上一次上一次注册信号处理函数的地址,第一次调用返回NULL 失败:返回SIG_ERR,设置 错误号

注意:SIGKILL与SIGSTOP不能被捕捉,不能被忽略

 #include<iostream>
  #include<sys/time.h>
  #include<signal.h>
  using namespace std;

  //捕捉到信号后输出信号的编号
  void myAlarm(int num)
  {
      cout<<"捕捉到的信号编号为:"<<num<<endl;
  }

  void test()
  {
      //注册信号,当定时器被触发后发送SIGALRM信号,之后被signal函数捕捉
      signal(SIGALRM,myAlarm);


      struct itimerval new_val;
      new_val.it_interval.tv_sec=3;
      new_val.it_interval.tv_usec=0;

      new_val.it_value.tv_sec=1;
      new_val.it_value.tv_usec=0;
      int ret=setitimer(ITIMER_REAL,&new_val,nullptr);
      cout<<"定时器开始了。。。"<<endl;

      if(ret==-1)
      {
          perror("error");
          exit(-1);
      }

      getchar();
  }

  int main()
  {
      test();
      return 0;
  }

自定义信号集

未决信号集:内核中所有没有被处理的信号,标志位为1表示该信号待处理 阻塞信号集合:用于说明未决信号集中响应的信号是否被阻塞,标志位为1表示该信号处于阻塞中

未决信号集中的信号只有处于没有被阻塞的情况下才能被处理

在 C++ 中,自定义信号集通常是通过使用 POSIX 标准库中的函数来实现的。下面是一些与自定义信号集相关的常用函数:

sigemptyset:用于初始化一个空的信号集。

#include <signal.h>
int sigemptyset(sigset_t *set);

sigfillset:用于将所有信号添加到信号集中。

#include <signal.h>
int sigfillset(sigset_t *set);

sigaddset:用于将指定的信号添加到信号集中。

#include <signal.h>
int sigaddset(sigset_t *set, int signum);

sigdelset:用于从信号集中删除指定的信号。

#include <signal.h>
int sigdelset(sigset_t *set, int signum);

sigismember:用于检查信号集中是否包含指定的信号。

#include <signal.h>
int sigismember(const sigset_t *set, int signum);
返回值:
    0:信号不在信号集
    1:信号在信号集
#include<iostream>
#include<signal.h>
using namespace std;

void check(int ret)
{
    if(ret==0)
        cout<<"不在信号集中,不阻塞"<<endl;
    else if(ret==1)
        cout<<"在信号集中,阻塞"<<endl;
}

void test()
{
    //自定义信号集
    sigset_t set;
    //清空信号集
    sigemptyset(&set);
    //判断SIGINT信号是否在信号集中
    int ret=sigismember(&set,SIGINT);
    check(ret);

    //添加信号到信号集中
    sigaddset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);

    ret=sigismember(&set,SIGINT);
    check(ret);

    ret=sigismember(&set,SIGQUIT);
    check(ret);

    //删除信号从信号集中
    sigdelset(&set,SIGINT);
    ret=sigismember(&set,SIGINT);
    check(ret);
}

sigpromask与sigpending函数

sigpromask

功能:设置或者修改信号的状态

原型

int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);

参数

  • how:指定方式

    • SIG_BLOCK:将自定义信号集set中的信号添加到内核信号集中,设置阻塞状态,即将自定义信号集中的信号设置wield阻塞

    • SIG_UNBLOCK:解除自定义信号set中信号的阻塞状态

    • SIG_SETMASK:直接以自定义信号集set的信号覆盖掉内核信号集

  • set:自定义信号集

  • oldset:存储旧的信号集

sigpending

功能:获取内核未决信号集的状态,若为1,说明该信号处于阻塞状态,等待处理

原型

int sigpending(sigset_t *set);

参数

  • set:传出参数,用于传出内核未决信号集的状态

示例

#include<unistd.h>
using namespace std;

void test()
{
    sigset_t set;//自定义信号集
    sigemptyset(&set);
    //将2号和3号新号添加到自定义信号集中
    sigaddset(&set,SIGINT);
    sigaddset(&set,SIGQUIT);
    //将这两个信号添加到内核阻塞信号集中,即在内核中将这两个信号设置为阻塞状态
    //在此状态下,当内核接收到这两个信号时将无法处理这两个信号
    sigprocmask(SIG_BLOCK,&set,nullptr);

    //获取内核未定义信号集,并打印
    int num=0;
    while(1)
    {
        num++;
        sigset_t pendset;
        sigemptyset(&pendset);
        //获取内核信号集状态,并通过pendset传出
        sigpending(&pendset);
        for(int i=1;i<31;i++)
        {
            if(sigismember(&pendset,i)==1)
                cout<<"1 ";
            else if(sigismember(&pendset,i)==0)
                cout<<"0 ";
            else{
                perror("sigismember");
                exit(-1);
            }
        }
        cout<<endl;
        sleep(1);
        if(num==10){
            //解除阻塞
            sigprocmask(SIG_UNBLOCK,&set,nullptr);
        }
    }

}

int main()
{
    test();
    return 0;
}

sigaction信号捕捉

功能:捕捉信号

原型

   #include <signal.h>
​
   int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

参数

  • signum:要捕捉的信号

  • act:捕捉到信号后的处理

    •        struct sigaction {
                 void     (*sa_handler)(int);//回调函数,定义捕捉信号后的处理行为,传入的参数为捕捉到的信号编号
                 void     (*sa_sigaction)(int, siginfo_t *, void *);//不常用,也是一个函数指针
                 sigset_t   sa_mask;//临时阻塞信号集,在信号捕捉函数的执行过程中,临时阻塞某些信号
                 //表示使用哪一个函数对捕捉到的信号进行处理
                 //若为0,表示使用第一个信号捕捉处理函数,为1则表示使用第二个信号捕捉处理函数
                 int        sa_flags;
                 //已经被弃用
                 void     (*sa_restorer)(void);
             };


  #include<iostream>
  #include<unistd.h>
  #include<sys/time.h>
  #include<signal.h>
  using namespace std;

  void myalarm(int num)
  {
      cout<<"get signal num is "<<num<<endl;
  }

  void test()
  {
      //注册信号
      struct sigaction act;
      act.sa_handler=myalarm;
      act.sa_flags=0;
      sigemptyset(&act.sa_mask);

      sigaction(SIGALRM,&act,nullptr);


      struct itimerval new_val;
      new_val.it_interval.tv_sec=3;
      new_val.it_interval.tv_usec=0;

      new_val.it_value.tv_sec=1;
      new_val.it_value.tv_usec=0;
      int ret=setitimer(ITIMER_REAL,&new_val,nullptr);
      cout<<"timer start..."<<endl;
      if(ret==-1)
      {
          perror("setitimer");
          exit(-1);
      }
      while(1);
  }

  int main()
  {
      test();
      return 0;
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值