进程间通信--信号

信号是进程间通信唯一的异步方式,是对中断的一种软件模拟。

信号的触发方式
1、终端触发。如CTRL+C产生SIGINT
2、硬件异常。如对无效内存操作触发SIGSEGV
3、kill函数、命令触发。
4、软件事件相关信号。如写一个已经关闭的管道或socket触发SIGPIPE;闹钟超时信号SIGALRM。

信号的操作方式
1、忽略信号:即内核将会丢弃该信号,但不会对目标进程产生任何影响。但SIGKILL和SIGSTOP信号不可以忽略,这两种信号向超级用户提供了终止或停止进程的可靠方法。
2、捕捉信号。用户可以自定义对信号的响应操作,但SIGKILL和SIGSTOP信号除外。
3、执行系统默认动作。

信号的种类
1、不可靠信号
不可靠指所发送的信号不一定被内核传递给目标进程,即信号可能会丢失。信号值在[1, 31]之间的所有信号。
2、不可靠信号
信号值在[SIGRTMIN,SIGRTMAX]之间的信号。
注意:32号信号和33号信号。这两个信号(SIGCANCEL和SIGSETXID)已被NPTL线程库使用了,用来实现线程的取消。

内核对信号的处理方式
可靠信号与不可靠信号的根本差异在于,内核收到信号后的处理方式。
不可靠信号,内核采用位图来记录该信号是否处于挂起状态。内核收到某不可靠信号,如果该信号已经处于未决状态,将丢弃。发送不可靠信号,信号可能会丢失,即内核递交给目标进程的次数,可能小于信号的发送次数。
可靠信号,内核采用队列来维护,如果收到可靠信号,内核将信号挂起到相应队列。不会丢失。但内核挂起信号的个数也有一定的限制,在一定的范围内,可靠信号不会被丢弃。

信号的可靠性实验

信号接收端:signal_receiver.c

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

static int sig_cnt[NSIG];
static volatile sig_atomic_t get_SIGINT = 0;

void handler(int signo)
{
    if (signo == SIGINT)
        get_SIGINT = 1;
    else
        sig_cnt[signo]++;
}

int main(int argc, char *argv[])
{
    int i = 0;
    sigset_t blockall_mask;
    sigset_t empty_mask;
    printf("%s: PID is %d\n", argv[0], getpid());

    for (i = 1; i < NSIG; i++)
    {
        if (i == SIGKILL || i == SIGSTOP || i == 32 || i == 33)
        {
            continue;
        }
        if (signal(i, &handler) == SIG_ERR)
        {
            fprintf(stderr, "signal for signo(%d) failed (%s)\n", i, strerror(errno));
        }
    }
    
    if (argc > 1)
    {
        int sleep_time = atoi(argv[1]);
        sigfillset(&blockall_mask);
        if (sigprocmask(SIG_SETMASK, &blockall_mask, NULL) == -1)
        {
            fprintf(stderr, "setprocmask to block all signal failed(%s)\n", strerror(errno));
            return -2;
        }
        printf("I will sleep %d second\n", sleep_time);
        sleep(sleep_time);
        sigemptyset(&empty_mask);
        if (sigprocmask(SIG_SETMASK, &empty_mask, NULL) == -1)
        {
            fprintf(stderr, "setprocmask to release all signal failed(%s)\n", strerror(errno));
            return -3;
        }
    }
    while (!get_SIGINT)
    {
        continue;
    }
    printf("%-10s%-10s\n", "signo", "times");
    printf("===================\n");
    for (i = 1; i < NSIG; i++)
    {
        if (sig_cnt[i] != 0)
        {
            printf("%-10d%-10d\n", i, sig_cnt[i]);
        }
    }
    return 0;
}```

信号发送端:signal_sender

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

void usage(void)
{
    fprintf(stderr, "USAGE:\n");
    fprintf(stderr, "=============================\n");
    fprintf(stderr, "signal_sender pid signo times\n");
}

int main(int argc, char *argv[])
{
    pid_t pid = -1;
    int signo = -1;
    int times = -1;
    int i;
    if (argc < 4)
    {
        usage();
        return -1;
    }
    pid = atol(argv[1]);
    signo = atoi(argv[2]);
    times = atoi(argv[3]);
    if (pid <= 0 || times < 0 || signo < 1 || signo >= 64 || signo == 32 || signo == 33)
    {
        usage();
        return -1;
    }
    printf("pid = %d, signo = %d, times = %d\n", pid, signo, times);
    for (i = 0; i < times; i++)
    {
        if (kill(pid, signo) == -1)
        {
            fprintf(stderr, "send signo(%d) to pid(%d) failed, reason(%s)\n", signo, pid, strerror(errno));
            return -2;
        }
    }
    fprintf(stdout, "done\n");
    return 0;
}

测试结果

在这里插入图片描述
如图,分别向接收端发送非实时信号(20)和实时信号(38)各10000次。
在这里插入图片描述
如图。接收端分别收到非实时信号(20)989次,实时信号(38)10000次,由此可见非实时信号会丢失。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值