signal 使用介绍


一、signal 信号简介

信号是软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式 。信号可以使一个正在运行的进程被另一个异步进程中断,转而处理突发事件
查看系统信号列表中,编号为 1 ~ 31 的信号为传统 UNIX 支持的信号,是不可靠信号(非实时的),编号为 32 ~ 63 的信号是后来扩充的,称做可靠信号(实时信号)
区别在于前者不支持排队,可能会造成信号丢失,而后者不会。非可靠信号一般都有确定的用途及含义, 可靠信号则可以让用户自定义使用
在这里插入图片描述

二、signal 产生方式

1. 用户操作终端

  • 终端上按 Ctrl+C 组合键通常产生中断信号 SIGINT
  • 终端上按 Ctrl+\ 键通常产生中断信号 SIGQUIT
  • 终端上按 Ctrl+Z 键通常产生中断信号 SIGSTOP

2. 硬件异常

除数为 0,无效的内存访问等。这些情况通常由硬件检测到并通知内核,然后内核产生适当的信号发送给相应的进程

3. 软件异常

当检测到某种软件条件发生,将其通知有关进程时产生信号

4. 调用 kill() 函数或者执行 kill 命令

kill 命令实际上是使用 kill() 函数来发送信号,接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户

三、signal 基本函数

1. int kill(pid_t pid, int signum);

给指定进程发送信号,接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者是超级用户
pid > 0: 将信号传送给进程 ID 为pid的进程
pid = 0: 将信号传送给当前进程所在进程组中的所有进程
pid = -1: 将信号传送给系统内所有的进程
pid < -1: 将信号传给指定进程组的所有进程,进程组号等于 pid 的绝对值
signum: 信号编号,这里可以填数字编号,也可以填信号的宏定义(kill -l 查看)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    int i = 0;

    pid = fork();
    if (0 == pid)
    {
        while (1)
        {
            printf("I am son\n");
            sleep(1);
        }
    }
    else
    {
        while (1)
        {
            printf("I am father\n");
            sleep(1);
            i++;
            if (3 == i)
            {
                kill(pid, SIGINT); // 向子进程发送中断信号 SIGINT
                // kill(pid, 2);   // 等价于 kill(pid, SIGINT);
            }
        }
    }
    return 0;
}

在这里插入图片描述

2. int pause(void);

等待接收信号,此函数会阻塞,将调用进程挂起直至捕捉到信号为止
没有产生信号前,进程一直阻塞在 pause() 不会往下执行,假如按“Ctrl+c”,pause() 会捕获到此信号,中断当前进程

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

int main(int argc, char *argv[])
{
    printf("in pause function\n");
    pause();
    return 0;
}

3. signal(int signum, sighandler_t handler);

一个进程收到一个信号的时候,可以用如下方法进行处理:

  • 执行系统默认操作,对大多数信号来说系统默认动作是用来终止该进程
  • 忽略此信号
  • 执行自定义处理函数,SIGKILLSIGSTOP 不能更改信号的处理方式,因为它们向用户提供了一种使进程终止的可靠方法
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void signal_handler(int signo)
{
    if (signo == SIGINT)
    {
        printf("recv SIGINT\n");
    }
    if (signo == SIGQUIT)
    {
        printf("recv SIGQUIT\n");
    }
}

int main(int argc, char *argv[])
{
    printf("wait for SIGINT OR SIGQUIT\n");
    signal(SIGINT, signal_handler);  // SIGINT: Ctrl+c
    signal(SIGQUIT, signal_handler); // SIGQUIT: Ctrl+\
    pause();
    pause();

    return 0;
}

在这里插入图片描述

4. int sigqueue(pid_t pid, int sig, const union sigval value);

给指定进程发送信号,与kill相比同时可以传递信息

union sigval
{
    int   sival_int;
    void *sival_ptr;
};

5. int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact );

设置或获取指定信号的处理(或同时执行这两种操作)
从 UNIX 系统继承过来的信号(SIGHUP~SIGSYS,前 32 个)都是不可靠信号,即非实时信号,不支持排队(多次发送相同的信号, 进程可能只能收到一次,可能会丢失)
SIGRTMIN 至 SIGRTMAX 的信号称为可靠信号,即实时信号,支持排队(发多少次, 就可以收到多少次, 不会丢失)
signal() 函数只能简单设置信号处理操作,只提供处理的信号和处理函数即可,主要用于前面 32 种不可靠、非实时信号的处理,并且不支持信息传递
sigaction() 函数可用来检查和更改信号处理操作,可以支持可靠、实时信号的处理,并且支持信号传递信息

struct sigaction
{
    void (*sa_handler)(int signum); /*简单的信号处理函数指针*/
    void (*sa_sigaction)(int signum, siginfo_t *info, void *context); /*复杂的信号处理函数指针*/
    sigset_t sa_mask;               /*信号阻塞集*/
    int sa_flags;                   /*信号处理方式*/
};

sa_handler/sa_sigaction:信号处理函数指针,根据情况给两者之一赋值:

  • SIG_IGN:忽略该信号
  • SIG_DFL:执行系统默认动作
  • 处理函数名:自定义信号处理函数
    sa_flags = SA_SIGINFO:使用 sa_sigaction 而不是 sa_handler 作为信号处理函数
// 信号发送
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    pid_t pid;
    union sigval tmp;

    pid = atoi(argv[1]);            // 发往的进程号
    tmp.sival_int = atoi(argv[2]);  // 信号携带的数据
    sigqueue(pid, SIGINT, tmp);     // 发送信号
    printf("pid_self = %d, pid_destination = %d\n", getpid(), pid);
    return 0;
}

// 信号接收
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

void signal_action(int signum, siginfo_t *info, void *ptr)
{
    printf("signum = %d\n", signum);
    printf("info->si_pid = %d\n", info->si_pid); // 发送方进程号
    printf("info->si_sigval = %d\n", info->si_value.sival_int); // 发送方传递的信息
}

int main(int argc, char *argv[])
{
    struct sigaction act, oact;

    act.sa_sigaction = signal_action; //指定信号处理回调函数
    sigemptyset(&act.sa_mask); // 阻塞集为空
    act.sa_flags = SA_SIGINFO; // 指定调用 sa_sigaction
    sigaction(SIGINT, &act, &oact);
    while (1)
    {
        printf("pid_self = %d\n", getpid());
        pause();
    }
    return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值