信号的发送与处理

在 Linux 中,信号是一种软件中断。当一个进程正在运行时,收到另一个进程的信号,会产生中断,转而去处理突发事件。Linux 中的信号可以用 kill -l 去查看。其中,1- 31号信号是古老 Unix 就支持的信号,属于不可靠信号(非实时信号,不支持排队,可能会造成信号丢失);而34 - 64 为可靠信号(实时信号,支持排队,不会造成信号丢失)。

[lingyun@manjaro ~]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

kill() 的使用

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

/* 功能:给指定的进程发送信号 */
/* 注意:使用 kill() 函数发送信号,接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者是超级用户。*/
int kill(pid_t pid, int sig);

/* 参数:
    pid > 0: 将信号传送给进程 ID 为pid的进程。
    pid = 0: 将信号传送给当前进程所在进程组中的所有进程。
    pid = -1: 将信号传送给系统内所有的进程。
    pid < -1: 将信号传给指定进程组的所有进程。这个进程组号等于 pid 的绝对值。
    signum: 信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令kill -l ("l" 为字母)进行相应查看。 
*/

/* 返回值:
    0:发送成功
    1:发送失败 
*/
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
    pid_t pid;

    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        _exit(-1);
    }
    else if(pid > 0)
    {
        for(int i = 0;;i++)
        {
            printf("父进程 --- %ds\n",i);
            sleep(1);
            if(i == 4)
            {
                kill(pid,SIGINT);
            }
        }
    }
    else if(pid == 0)
    {
        while(1)
        {
            printf("子进程\n");
            sleep(1);
        }
    }

    return 0;    
}

执行后可见,4s 后子进程退出了,因为父进程给子进程发送了 SIGINT 信号。

[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
父进程 --- 0s
子进程
父进程 --- 1s
子进程
父进程 --- 2s
子进程
子进程
父进程 --- 3s
子进程
父进程 --- 4s
子进程
父进程 --- 5s
父进程 --- 6s
父进程 --- 7s
父进程 --- 8s
父进程 --- 9s
父进程 --- 10s
^Z
[3]+  已停止               ./study
[lingyun@manjaro study]$

信号的处理

#include <signal.h>

/* 回调函数的声明,我们自定义信号处理函数时,用这种形式 */
typedef void (*sighandler_t)(int);

/* 注册信号处理函数(不可用于 SIGKILL、SIGSTOP 信号),即确定收到信号后处理函数的入口地址。此函数不会阻塞。 */
sighandler_t signal(int signum, sighandler_t handler);

/* 参数:
signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令 kill -l ("l" 为字母)进行相应查看。
handler: 取值有 3 种情况:
    SIG_IGN:忽略该信号
    SIG_DFL:执行系统默认动作
    信号处理函数名:自定义信号处理函数(回调函数)
*/

/* 返回值:
成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回值,必须在前面先声明此函数指针的类型。
失败:返回 SIG_ERR
*/

看下面的例子:

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

/* 回调函数 */
void signal_handle(int signum)
{
    if(signum == SIGINT)
    {
        printf("收到 SIGINT 信号\n");
    }
    else if(signum == SIGQUIT)
    {
        printf("收到 SIGQUIT 信号\n");
    }
}

int main()
{
    /* 注册信号,绑定回调函数 */
    signal(SIGINT, signal_handle);
    signal(SIGQUIT, signal_handle);

    printf("等待信号......\n");
    
    /* pause 用于等待信号,会阻塞,直到有信号到达 */
    pause();
    pause();

    return 0;
}

执行可见,执行到第一个 pause 后阻塞,直到接收到信号 SIGINT 才继续。第二个 pause 阻塞,知道接收到 SIGQUIT 才继续。

[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
第一个 pause 等待信号......
^C收到 SIGINT 信号
第二个 pause 等待信号......
^\收到 SIGQUIT 信号
[lingyun@manjaro study]$ 

再测试一下 signal() 函数的返回值:

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

//typedef void (*sighandler_t) (int);

void signal_handle1(int signum)
{
    printf("接收到 SIGINT 信号\n");
}

void signal_handle2(int signum)
{
    printf("接收到 SIGQUIT 信号\n");
}

int main()
{
    sighandler_t last_call_func = NULL;

    last_call_func = signal(SIGINT, signal_handle1);
    if(last_call_func == NULL)
    {   /* 第一次注册 SIGINT 信号,返回值为 NULL */
        printf("上次的回调函数的地址是NULL\n");
    }
    else if(last_call_func == signal_handle1)
    {
        printf("上次的回调函数的地址是 %p\n",last_call_func);
    }

    last_call_func = signal(SIGINT, signal_handle1);
    if(last_call_func == NULL)
    {
        printf("上次的回调函数的地址是NULL\n");
    }
    else if(last_call_func == signal_handle1)
    {   /* 第二次注册 SIGINT 信号,返回值是 signal_handle1 */
        printf("上次的回调函数的地址是 %p\n",last_call_func);
    }

    last_call_func = signal(SIGQUIT, signal_handle2);
    if(last_call_func == NULL)
    {   /* 第一次注册 SIGQUIT 信号,返回值为 NULL */
        printf("上次的回调函数的地址是NULL\n");
    }
    else if(last_call_func == signal_handle2)
    {
        printf("上次的回调函数的地址是 %p\n",last_call_func);
    }

    last_call_func = signal(SIGQUIT, signal_handle2);
    if(last_call_func == NULL)
    {   /* 第二次注册 SIGQUIT 信号,返回值为 signal_handle2 的地址 */
        printf("上次的回调函数的地址是NULL\n");
    }
    else if(last_call_func == signal_handle2)
    {
        printf("上次的回调函数的地址是 %p\n",last_call_func);
    }
    return 0;
}

 执行可见,每个信号的第一次注册返回值均为 NULL,后面再注册则返回上一次注册时的回调函数的地址。

[lingyun@manjaro study]$ gcc study.cpp -o study
[lingyun@manjaro study]$ ./study
上次的回调函数的地址是NULL
上次的回调函数的地址是 0x5562b26b0159
上次的回调函数的地址是NULL
上次的回调函数的地址是 0x5562b26b0173
[lingyun@manjaro study]$ 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值