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在每个进程中都是独立的,互不干扰,所以在信号函数的判断都是独立的

相关推荐
此生只爱蛋13 分钟前
【Linux】正/反向代理
linux·运维·服务器
qq_54702617919 分钟前
Linux 基础
linux·运维·arm开发
zfj32125 分钟前
sshd除了远程shell外还有哪些功能
linux·ssh·sftp·shell
废春啊32 分钟前
前端工程化
运维·服务器·前端
我只会发热35 分钟前
Ubuntu 20.04.6 根目录扩容(图文详解)
linux·运维·ubuntu
爱潜水的小L1 小时前
自学嵌入式day34,ipc进程间通信
linux·运维·服务器
保持低旋律节奏1 小时前
linux——进程状态
android·linux·php
zhuzewennamoamtf1 小时前
Linux I2C设备驱动
linux·运维·服务器
zhixingheyi_tian1 小时前
Linux 之 memory 碎片
linux
邂逅星河浪漫1 小时前
【域名解析+反向代理】配置与实现(步骤)-SwitchHosts-Nginx
linux·nginx·反向代理·域名解析·switchhosts