Linux C 信号处理

信号是传递给进程的一种事件通知,也称作软中断。比如在终端按下ctrl+c可以结束进程,实际上就是给进程发送了一个SIGINT信号;程序发生错误的时候,比如除数为0,非法访问内存的时候,内核也会向发生异常的那个进程传递错误信号。

有关信号的术语:

  1. 当发生了一个需要引起进程注意的时间而导致信号出现,我们称对此进程发送信号
  2. 当被发送的那个进程识别了信号并且采取了适当动作时,我们称信号交付给了进程,或者成进程接受了信号。
  3. 信号交付给进程后,进程可以通过一个函数捕获该信号并进行下一步操作。这个捕获函数就称作信号句柄
  4. 当一个信号已经生成,但还未交付,我们称这个信号是悬挂的。
  5. 每个进程都有一个信号屏蔽字标识当前被阻塞 交付的信号集合。如果某个信号对应的位被设置,则该型号是被阻塞

如何发送信号?

raise函数:
#include <signal.h>
int raise(int sig);

给本进程发送名为sig的信号。如果安装了sig句柄函数,则raise()只会在句柄函数返回时才会返回

kill函数:
#include <signal.h>
int kill(pid_t pid, int sig);

给pid号进程发送名为sig的信号,如果进程不存在,则返回-1。因此kill函数经常用来检测进程是否存在

如何接受信号?

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

sig指明是哪一种信号;handler可以是函数指针,也可以是如下的宏:

  • SIG_DFL:采取默认动作
  • SIG_IGN:忽略该信号
sigaction函数

signal函数缺乏可靠性,且功能局限。一般采用sigaction函数。

#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);

act和oact都是指向sigaction结构体的指针。act规定发送信号的动作;oact可以为NULL,如果不为NULL,则可用用来接收之前的动作对象。
sigaction是描述信号动作的一个结构,定义为:

struct sigaction
{
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t*, void*);
    sigset_t sa_mask;
    int sa_flags;
};
  • sa_handler与signal()的第二个参数相同,指定一个信号句柄,其值也可以是SIG_DFL、SIG_IGN
  • sa_sigaction也是一个信号句柄,是一个更高级的信号句柄,只用当sa_flags 设为SA_SIGINFO时起作用。

示例代码

//信号的中断测试

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void Handler(int sig, siginfo_t *info, void *context)
{
    if (sig == SIGALRM)
    {
        for (int i = 0; i < 10; i++)
        {
            printf("CallBack is running...\n");
            sleep(1);
        }
    }
}

int main()
{
    struct sigaction sa;
    sa.sa_sigaction = Handler;
    sa.sa_flags = SA_RESTART | SA_SIGINFO;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGALRM, &sa, NULL) == -1)   //设定闹钟信号
    {
        printf("error\n");
        return -1;
    }

    alarm(2);     //两秒后将发送闹钟信号

    for (int i = 0; i < 10; i++)
    {
        printf("main is running...\n");
        sleep(1);
    }

    return 0;
}
运行结果:

运行结果
可以看出,进程接收信号后会立即中断当前操作,转而执行信号句柄。

设置信号屏蔽

每一个进程都有一个信号屏蔽,进程在创建是继承父进程的信号屏蔽。

sigset_t类型及其操作

sigset_t称为信号集类型,是多个型号的集合。它只能用一下几个函数操纵:

int sigemptyset(sigset_t *set);                     //置为空集
int sigfillset(sigset_t *set);                      //使之包含所有信号
int sigaddset(sigset_t *set, int signum);               //添加信号
int sigdelset(sigset_t *set, int signum);               //删除信号
int sigismember(const sigset_t *set, int signum);       //测试信号是否在信号集中
信号屏蔽函数sigprocmask()
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

how的值指明如何改变信号屏蔽,它必须是下列的值之一:

  • SIG_BLOCK:阻塞set集中的信号
  • SIG_UNBLOCK:放开set集中的信号
  • SIG_SETMASK:用set所指的信号集进程的新信号集屏蔽

oset作为输出参数,可以为NULL,输出之前该进程屏蔽的信号集。

示例代码

//信号屏蔽测试

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void SignalHandler(int sig)
{
    if (sig == SIGUSR1)
    {
        printf("signal1 is clicked\n");
    }
    else if (sig == SIGUSR2)
    {
        printf("signal2 is clicked\n");
    }
}

int main()
{
    struct sigaction sa;
    sa.sa_handler = SignalHandler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGUSR1, &sa, NULL) == -1){
        printf("error\n");
        return -1;
    }
    if (sigaction(SIGUSR2, &sa, NULL) == -1){
        printf("error\n");
        return -1;
    }
/*  这几行代码回导致进程屏蔽SIGINT和SIGQUIT从而无法退出
    sigset_t mask_quit;
    sigemptyset(&mask_quit);
    sigaddset(&mask_quit, SIGINT);
    sigaddset(&mask_quit, SIGQUIT);
    if (sigprocmask(SIG_BLOCK, &mask_quit, NULL) < 0)
    {
        printf("mask quit error\n");
        return -1;
    }
*/

    sigset_t st;
    sigemptyset(&st);
    sigaddset(&st, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &st, NULL) < 0)
    {
        printf("mask sig2 error\n");
        return -1;
    }

    printf("raise SIGUSR1\n");
    raise(SIGUSR1);
    printf("raise SIGUSR2\n");
    raise(SIGUSR2);


    while (1);
    return 0;
}
运行结果

运行结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值