Linux进程间通信第三讲 信号signal kill

目录

三、信号(signal)

3.1 概念

3.2 信号的处理

3.3 信号的发送

3.4 信号的屏蔽


 

三、信号(signal)

3.1 概念

信号本质上是一种软件中断

软件触发的中断、和硬件的处理机制一样

当收到信号时, 停下来手头的事情,去处理信号处理函数,处理完后,再继续做原来的事情

 

信号处处都在

其实我们已经看到过

1、程序在运行时, 我们想退出, 按“ctrl + c”时就已经发送了一个信号出去

2、段错误

3、除0异常信号(代码中出现整数除以0)

4、杀死一个进程的指令“kill -9 pid” , kill 就是 发送信号的指令, -9 是9号信号, pid是进程号

5、子进程结束后, 发送信号给父进程,来回收它的资源

 

如果你觉得只有那么一点, 那就错了

来!

执行下面这个指令,   可以查看本系统支持的所有的信号

kill -l 

Linux 有64个信号

每个信号会给一个宏名, 开发中, 尽量使用宏名, 不使用数字。宏名以 SIG开头, 例如9号信号的名字 为 SIGKILL

Linux 的信号分两类: 可靠信号(支持排队) 不可靠信号(不支持排队)  

当一个进程同时收到多个相同信号时, 可靠信号都可以收到 , 而不可靠信号会丢失

1-31属于不可靠信号, 34 -64属于可靠信号。

 

 

3.2 信号的处理

当一个进程收到信号的时候

它有三种处理方式:

    1 ) 默认(缺省)处理

            默认处理, 而大多数信号的默认处理方式为 退出进程

    2 ) 忽略信号

            收到的信号不做任何处理

    3 ) 捕获信号

            收到信号后会去执行信号处理函数,而这个函数可由程序猿自己编写

 

Linux提供了一个函数来设置信号的处理方式 : signal 函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

参数如下:

            signum     -     设置哪个信号

            handler    -      信号的处理方式

                        SIG_DFL    -    默认处理

                        SIG_IGN    -     忽略信号   

                        函数            -     捕获信号

                       

 

前面两种方式忽略不讲, 主要讲解第三种方式, 即

当设置为 捕获信号 的方式时

需要自己额外写个函数(信号处理函数)

收到信号后, 程序会停下当前的事情去处理这个函数

信号处理函数 如何定义?

它的格式为 无返回值, 一个int 型的参数, 函数名自定义

代码如下(signal、捕获信号方式):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

//信号处理函数
void func(int sig)
{
    printf("sig %d coming!\n",sig);
}


int main()
{
    printf("pid = %d\n",getpid());
    //默认处理
    //signal(SIGINT,SIG_DFL);
    //忽略信号
    //signal(SIGINT,SIG_IGN);
    //signal(SIGKILL,SIG_IGN);//忽略信号9是无效的, 如果这样的话, 电脑病毒不就很嚣张了?关不掉

    //捕获信号
    signal(SIGINT,func);


    while(1);
    return 0;
}

把程序运行起来, 键盘按“ctrl + c”, 即发送了信号2 (SIGINT), 等价于指令“kill -2  pid”

输出结果如下:

pid = 3000
^Csig 2 coming!

 

但并不是所有的信号都可以处理,有两个特殊的信号

信号9(SIGKILL)和信号19(SIGSTOP)既不能忽略, 也不能捕获, 只能默认处理

信号9默认退出

信号19默认暂停

 

另外

假如父进程的信号处理方式设置好了

那么它的子进程是如何继承父进程的信号处理方式呢?

1、fork  /  vfork 创建的子进程, 是完全继承父进程的处理方式

2、(fork  /  vfork) + exec系列函数创建的子进程, 只有捕获信号的方式不继承,由于exec替换了进程内容,这个函数就不存在了

关于fork/vfork/exec 多进程编程 在之前的博客有系统进行讲解(链接)

 

3.3 信号的发送

1、用键盘发送信号

        ctrl + c ---> 信号2(SIGINT)

        ctrl + \  ---> 信号3(SIGQUIT)

        ctrl + z ---> 信号20(SIGTSTP)

2、硬件故障、程序错误

        段错误

        总线错误

        除以0

3、kill/killall 命令

4、通过函数发送信号

        4.1 kill函数

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

        给进程号为pid的进程发送sig信号, 代码如下

/*
这个程序是用来杀死其它进程的
执行这个程序的时候,应该这么执行:
    ./可执行文件  pid

*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

int main(int argc,char **argv)
{
    printf("pid = %d\n",getpid());
    if(argc!=2){
        printf("Usage:%s PID\n",argv[0]);
        exit(-1);
    }

    kill(atoi(argv[1]),SIGINT);

    return 0;
}

 

        4.2 raise 函数 给自己发送信号

        4.3 alarm 函数 让进程等待一段时间(second) , 然后发送指定信号(14)SIGALRM给自己。         

              参数为设置的秒数。

              返回值为 当前存在闹钟剩余的时间。

              SIGALRM 默认让进程结束。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void fuc(int sig)
{
        printf("Remember to fetch vegetable!\n");
        alarm(1);
}

int main()
{
        signal(SIGALRM,fuc);

        alarm(10);

        while(1);

        return 0;
}

        4.4 pause 函数 会导致进程睡眠, 不再使用cpu, 和while( 1) 有本质区别。

             功能是等待, 只有非忽略信号( 捕获信号或者终止信号)才会唤醒。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void func(int sig)
{
    printf("收到信号%d!\n",sig);
}

int main(int argc,char **argv)
{
    signal(SIGINT,func);
    printf("pid = %d\n",getpid());

    //等待一个非忽略信号来唤醒进程,此处唤醒的信号为SIGINT
    int res = pause();
    printf("res = %d\n",res);
    
    return 0;
}

 

3.4 信号的屏蔽

信号会打断原来的程序 去执行其它程序。

但是在执行关键代码( 例如初始化)时, 收到信号被打断 ,可能会导致错误的发生

进程无法阻止信号的到来, 但是可以暂时不处理它, 即屏蔽信号, 就是信号到来后暂时不处理, 当关键代码执行完毕后, 再解除屏蔽, 最后再去处理来了的信号。

信号屏蔽中使用了信号集

 

信号集

就是一个信号的集合

类型 sigset_t  (我前面也有提到 ,什么什么下划线t的数据类型都是正整数)

这个数的二进制形式中, 每一位对应一个信号(0表示没有、1表示有)

通过这个数, 就可以记录要屏蔽的信号了

Linux也提供了相关的函数接口:

我们通过代码熟悉一下函数的用法:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

int main()
{
    printf("sigset_t有%d 字节\n",sizeof(sigset_t));

    sigset_t set;
    sigemptyset(&set);//清空
    printf("%lld\n",set);
    
    //添加信号
    sigaddset(&set,SIGINT);//0000 0010  信号2记录在了第2位(置1)
    printf("%lld\n",set);   
    sigaddset(&set,SIGQUIT);//0000 0110 信号3记录在了第3位(置1)
    printf("%lld\n",set);
    sigaddset(&set,6);//0010 0110       信号6记录在了第6位(置1)
    printf("%lld\n",set);



    //删除信号
    sigdelset(&set,SIGQUIT);//将第3位 复位为0
    printf("%lld\n",set);//0010 0010    34

    //查找信号
    if(sigismember(&set,SIGINT))
        printf("信号集中有信号2!\n");
    if(sigismember(&set,SIGQUIT))
        printf("信号集中有信号3!\n");

    return 0;
}

 

我们学会了记录, 但这还没完

我们要利用它来开始屏蔽信号啦

屏蔽信号期间, 是可以收信号, 但是是不处理的,等屏蔽结束后才会去处理

屏蔽信号期间, 假如收到多个同样的信号, 等屏蔽结束后, 相当于同时一起来.  这对于可靠信号来说, 它是支持排队的, 有多少就处理多少, 而对于不可靠信号来说, 这会丢失信号

代码如下,当执行普通代码时, 可以收到指定信号, 但是执行关键代码时需要屏蔽,屏蔽期间收到的指定信号不处理,屏蔽结束后再去处理

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

//自定义信号处理函数
void func(int sig)
{
    printf("捕获了信号%d\n",sig);
}

int main()
{
    printf("pid = %d\n",getpid());
    //设置信号处理方式 为捕获信号
    signal(SIGINT,func);
    signal(45,func);

    //假设执行普通代码
    printf("执行普通代码,不屏蔽信号!\n");
    sleep(8);//sleep休眠期间, 不占cpu,但是一旦非忽略信号来,便唤醒, 不再休眠(不再等待)
    printf("普通代码执行完毕!\n");

    //记录要屏蔽的信号
    sigset_t set,oldset;
    sigemptyset(&set);
    sigaddset(&set,SIGINT);
    sigaddset(&set,45);
    
    //开始屏蔽
    printf("执行关键代码,信号屏蔽!\n");
    sigprocmask(SIG_SETMASK,&set,&oldset);


    sleep(6);
    //屏蔽期间可以查看收到的信号
    sigset_t ar_set;
    sigpending(&ar_set);
    if(sigismember(&ar_set,SIGINT))
        printf("收到了信号2\n");
    if(sigismember(&ar_set,45))
        printf("收到了信号45\n");
    sleep(6);    

    //结束屏蔽
    printf("关键代码执行完毕,解除信号屏蔽!\n");
    sigprocmask(SIG_SETMASK,&oldset,NULL);

    while(1);
    return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玖零猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值