Linux | 信号的发送及响应

目录

信号的发出及响应

1.对信号做出响应 signal

(1)调用signal()函数

(2)3种响应方式

2.信号的发出 kill

(1)kill()发送信号

(2)辅助知识:字符串转整型sscanf

3. 父子进程间的信号|17

(1)子进程结束,内核默认给父进程发送17信号。

(2)收到信号处理僵死进程

二、发送信号的实质 


信号的发出及响应

发送信号:即通知进程产生了某种事件。比如中断信号SIGINT(#define SIGINT 2),键盘按下ctrl+c(结束进程),会产该信号。(程序出错退出,大部分情况是由于收到某个信号导致的。)

1.对信号做出响应 signal

(1)调用signal()函数

参数1为信号代号,参数2为函数指针(指针指向返回值为void,参数为int的回调函数)   实质:收到信号signum代号即调用参数2指向的函数。

(2)3种响应方式

对于收到信号的响应方式有三种:默认(SIG_DFL)、忽略(SIG_IGN)、自定义(自己写的信号处理函数)

#define  SIG_DFL  (void(*)(int))0  //默认(本质仍为函数指针,将0强转为函数指针)

#define  SIG_IGN   (void(*)(int))1  //忽略(本质仍为函数指针,将1强转为函数指针)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void fun_sig(int sig)
{
    printf("sig=%d\n",sig);
    signal(sig,SIG_DFL);//设置收到信号SIGINT采取默认方式响应(ctrl+c~结束进程)
}
int main()
{
    //收到信号SIGINT,转到内核态传参调用回调函数(等价于fun_sig(SINGINT))。执行完后返回继续执行while循环。
    signal(SIGINT,fun_sig);

    //signal(SIGINT,SIG_IGN);//收到信号SIGINT采取忽略的方式进行响应
    while(1)
    {
        printf("main run\n");
        sleep(1);
    }
    //ctrl+\可以结束进程 相当于exit(1);
}

避免忽略所有信号,规定收到信号:9(kill -9),就会结束进程(杀死进程)!!!

2.信号的发出 kill

kill -9+进程号:发出9号信号,强制杀死进程。  使用kill+进程号杀死进程,默认发送的是信号15!

(1)kill()发送信号

kill:发送信号! int kill(pid_t pid,int sig);     返回0代表成功,-1代表失败。

参数1:pid为进程ID号(可以使用ps -ef | grep main  过滤查找正在运行的main进程 )

参数2:sig为给该进程发送的信号。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(int argc,char*argv[]) //argv[0]="./mykill",argv[1]="pid",argv[2]=信号代号
{
    if(argc!=3)
    {
        printf("main arg error\n");
        exit(1);
    }
    int pid=0;
    int sig=0;
    sscanf(argv[1],"%d",&pid);//ssanf()字符串转整型存储到变量pid中
    sig=atoi(argv[2]); 
    if(kill(pid,sig)==-1)
    {
        printf("kill error\n");
    }
    exit(0);
}

(2)辅助知识:字符串转整型sscanf

sscanf() 将参数1字符串转为整型(参数二"%d")存储下来(存储在普通变量中应该加取地址符&):

int a=atoi(字符串);//字符串传参调用atoi()转为整型赋值给a。

3. 父子进程间的信号|17

(1)子进程结束,内核默认给父进程发送17信号。

#include<stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void fun(int sig)
{
    printf("sig=%d\n",sig);//收到信号SIGCHLD传参调用该函数~代表子进程已结束,变为僵死进程
    int val=0;
    int id=wait(&val);//将子进程退出码写入到val变量中,wait()函数返回值为捕获到的子进程pid
    printf("child id=%d,exit code=%d\n",id,WEXITSTATUS(val));//WEXITSTATUS()获取子进程退出码
}
int main()
{
    signal(SIGCHLD,fun);//子进程结束后,内核默认给子进程发送信号SIGCHLD
    int n=0;
    char* s=NULL;
    pid_t pid=fork();
    if(pid==-1)
    {
        exit(1);
    }
    if(pid==0)
    {
        n=3;//子进程先结束
        s="child";
    }
    else
    {
        n=7;
        s="parent";
    }
    for(int i=0;i<n;i++)
    {
        printf("s=%s\n",s);
        sleep(1);
    }
    exit(0);
}

(2)收到信号处理僵死进程

收到17信号代表子进程结束,子进程先结束会变为僵死进程,正好可以在这时处理僵死进程:调用wait()处理僵死进程+获取退出码,调用WEXITSTATUS()打印退出码。

调用wait(),成功返回值为子进程的id号。 

int val=0;

int id=wait(&val); //将退出码存在val中

不能直接打印val,val>>8=WEXITSTATUS(val); //获取子进程退出码

或者可以不用获取打印子进程退出码,只需要处理僵死进程,可以直接wait(NULL); 

二、发送信号的实质 

PCB进程控制块的本质为结构体struct task_task{},其中有一个成员long signal;

kill(pid,SIGINT); //通过pid可以唯一确定PCB,发送信号2会将结构体中变量signal的第二位(由低位开始数)由0置为1(因此信号值不会超过长整型的位数32)。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值