Linux-信号

信号是由用户,系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。

进程分为前台进程和后台进程,对于前台进程我们可以输入特殊的终端字符来给它发送信号,比如输入Ctrl+c,发送一个中断信号

系统异常,比如浮点异常和非法内存段访问

运行kill命令或者调用kill函数

发送信号

Linux下,一个进程给一个进程发送信号的api是kill函数

cpp 复制代码
int kill(pid_t pid,int sig);

sig是指定的信号,pid是指定的进程

pid>0 信号发送给指定的进程

pid=0. 信号发送给进程组的其他进程

pid=-1 信号发送给除init进程外的所有进程

pid<-1 信号发送给组ID为-pid的进程组中的所有成员

有发送信号,那肯定得有一个接收信号的东西

接收信号

signal系统调用

cpp 复制代码
       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);

signum是信号的种类

这里我们可以写数字也可以写具体的名称

handler是一个函数指针,传入函数名就行

下面实现一个信号的安装和发送

cpp 复制代码
void handle(int v)
{
    cout << "信号被触发.." << v << endl;
}

void demo4()
{
    int pid = 0;
    pid = fork();
    if (pid > 0)
    {
        // 信号安装

        signal(SIGUSR1, handle);
        while (1)
            ;
    }
    else if (pid == 0)
    {
        // 发送信号
        sleep(1);
        kill(getppid(), SIGUSR1);
        while (1)
            ;
    }
}

父进程进行信号安装,子进程进行信号的发送

带参信号函数

上面的是简单的信号发送,但是不能带参数,只能是发送一个信号过去

sigaction系统调用

cpp 复制代码
       #include <signal.h>

       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

安装信号的函数变了,那么发送信号的函数也应该发生变化

cpp 复制代码
  #include <signal.h>

       int sigqueue(pid_t pid, int sig, const union sigval value);

前两个参数就不用说了,主要是最后一个是一个联合体

cpp 复制代码
  union sigval {
               int   sival_int;
               void *sival_ptr;
           };

填对应的参数就行了

下面来一个带参数的信号发送和接收

cpp 复制代码
void demo5_handel(int v, siginfo_t *info, void *context)
{
    cout << "v:" << v << endl;
    cout << "value:" << info->si_value.sival_int << endl;
}

void demo5()
{
    int pid = 0;
    pid = fork();
    if (pid == 0) // 子进程,安装
    {
        struct sigaction action;
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = demo5_handel;
        sigaction(SIGUSR1, &action, NULL);
        while (1)
            ;
    }
    else if (pid > 0) // 父进程,发送
    {
        sleep(1);
        union sigval val;
        val.sival_int = 10000;
        sigqueue(pid, SIGUSR1, val);
        while (1)
            ;
    }
}

下面来一个难一点的demo

有ABCD四个进程

A携带数据1000通过SIGUSR1发给进程B

B收到数据以后对数据进行+1操作再次携带数据1001通过SIGUSR2发给进程C

C收到数据以后对数据进行+1操作再次携带数据1002通过SIGRTMIN发给进程D

D收到数据以后对数据进行+1操作再次携带数据1003通过SIGUSR2发给进程A

A收到数据以后将最终的数据打印到控制台

首先我的思想是主进程为A,A创建B进程,B创建C,依此下去,这里我们使用一个信号函数,对于信号的区分我们在代码中理解

zh

cpp 复制代码
int fatherpid;
int childpid;


void handel_(int signo,siginfo_t *info,void *context)
{
    if(signo==SIGUSR2)
    {
        if(fatherpid==0)
        {
            cout<<"进程A收到SIGUSR2信号 value="<<info->si_value.sival_int<<endl;
        }
        else 
        {
            cout<<"进程C收到SIGUSR2信号 value="<<info->si_value.sival_int<<endl;
            union sigval val;
            val.sival_int=info->si_value.sival_int+1;
            sigqueue(childpid,SIGRTMIN,val);
        }
    }
    else if(signo==SIGUSR1)
    {
         cout<<"进程B收到SIGUSR1信号 value="<<info->si_value.sival_int<<endl;
            union sigval val;
            val.sival_int=info->si_value.sival_int+1;
            sigqueue(childpid,SIGUSR2,val);
    }
    else if(signo==SIGRTMIN)
    {
         cout<<"进程D收到SIGRTMIN信号 value="<<info->si_value.sival_int<<endl;
            union sigval val;
            val.sival_int=info->si_value.sival_int+1;
            sigqueue(pidA,SIGUSR2,val);
    }

}

void demo9()//信号作业再次复现
{
    pidA=getpid();
    pidB=fork();
    if(pidB==0){
        fatherpid=getppid();
        struct sigaction action;
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = handel_;
        sigaction(SIGUSR1, &action, NULL);
        pidC=fork();
        childpid=pidC;
        if(pidC==0){
            fatherpid=getppid();
            
            struct sigaction action;
            action.sa_flags = SA_SIGINFO;
            action.sa_sigaction = handel_;
            sigaction(SIGUSR2, &action, NULL);
            pidD=fork();
            childpid=pidD;
            if(pidD==0){
                fatherpid=getppid();
                struct sigaction action;
                action.sa_flags = SA_SIGINFO;
                action.sa_sigaction = handel_;
                sigaction(SIGRTMIN, &action, NULL);
                while(1);
            }
            while(1);
        }
        while(1);
    }
    else if(pidB>0)
    {
        childpid=pidB;
        struct sigaction action;
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = handel_;
        sigaction(SIGUSR2, &action, NULL);

        sleep(3);
        union sigval val;
        val.sival_int=10000;
        sigqueue(pidB,SIGUSR1,val);
        while(1);
    }
}

这里设置了一个父进程id,子进程id的值用来区分信号,比如A进程只有子进程,D进程只有父进程,对于B和C来说不用区分

注意这里的fatherpid和childpid在每个进程中都是独立的,互不干扰,所以在信号函数的判断都是独立的

相关推荐
Leinwin4 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
2401_865382504 小时前
信息化项目运维与运营的区别
运维·运营·信息化项目·政务信息化
漠北的哈士奇4 小时前
VMware Workstation导入ova文件时出现闪退但是没有报错信息
运维·vmware·虚拟机·闪退·ova
如意.7594 小时前
【Linux开发工具实战】Git、GDB与CGDB从入门到精通
linux·运维·git
Thera7775 小时前
C++ 高性能时间轮定时器:从单例设计到 Linux timerfd 深度优化
linux·开发语言·c++
运维小欣5 小时前
智能体选型实战指南
运维·人工智能
yy55275 小时前
Nginx 性能优化与监控
运维·nginx·性能优化
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ6 小时前
Linux 查询某进程文件所在路径 命令
linux·运维·服务器
05大叔7 小时前
网络基础知识 域名,JSON格式,AI基础
运维·服务器·网络
安当加密8 小时前
无需改 PAM!轻量级 RADIUS + ASP身份认证系统 实现 Linux 登录双因子认证
linux·运维·服务器