(三十二)信号——信号处理函数

  要明白信号处理函数的使用,就要先知道信号的捕捉设定,什么时候会调用信号处理函数和其执行的流程是什么,下图完整的展示了信号捕捉的设定,以及信号处理函数的触发机制:
这里写图片描述
  所以,从上图可以知道,只有当程序中断,异常或系统调用,才会进入内核态,也只有进入了内核态才能处理信号,在这里初学者常常有一个误区,他们会觉得如果我在程序中没有设置中断、不出现异常、不使用系统调用,那就就不会进入内核态,也就不会处理信号了!这是对操作系统错误的理解,由于linux是分时操作系统(但可以改成实时的如:UCOS就是linux修改而来的实时系统,很多人在此有误区),所以当时间片用完时,就会进入内核态,进行进程调度,在此就会处理信号了!
  
  内核给我们提供了一个用于注册用户自定义信号处理函数 的函数sigaction:

#include <signal.h>

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

struct sigaction 定义:
struct sigaction {
        void (*sa_handler)(int);
        void (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t sa_mask;
        int sa_flags;
        void (*sa_restorer)(void);
};

sa_handler :   早期的捕捉函数
sa_sigaction : 新添加的捕捉函数,可以传参 , 和sa_handler互斥,两者通过sa_flags选择采用哪种捕捉函数
sa_mask :  在执行捕捉函数时,设置阻塞其它信号,sa_mask | 进程阻塞信号集,退出捕捉函数后,还原回原有的阻塞信号集
          (即用于信号处理函数中是否还处理信号的到达)
sa_flags : SA_SIGINFO 或者 0,上面两个函数调用哪一个,0调用sa_handler所指向的函数,1代表sa_sigaction所指向的函数
sa_restorer : 保留,已过时

所以我们要做的步骤是:
1)声明sigaction 结构体
2)构造sigaction 结构体
3)用sigaction 函数注册sigaction 结构体

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

/*
 * #include <signal.h>
 * int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
 * 
 * struct sigaction 定义:
 * struct sigaction {
 * void (*sa_handler)(int);
 * void (*sa_sigaction)(int, siginfo_t *, void *);
 * int sa_flags;  //上面两个函数调用哪一个,0调用最上面那个,1代表调用上面那
 * //在信号处理函数中的信号集,信号处理函数运行完之后就恢复原有信号集
 * sigset_t sa_mask;
 * void (*sa_restorer)(void);//现在已经废弃使用了
 *};
 */

void do_sig(int num)
{
    int n = 3;
    printf("i am do_sig\n");
    while(n--)
    {
        printf("n = %d", n);
        sleep(1);
    }
    printf("num = %d\n",num);
}

int main(void)
{
/*****************1.声明sigaction结构体****************/
    struct sigaction act;    
/****************************************************/

/*****************2.构造sigaction结构体****************/
    //标记信号处理函数
    //act.sa_handler = SIG_DFL;  //默认方式
    //act.sa_handler = SIG_IGN;  //忽略方式
    act.sa_handler = do_sig;     //捕捉方式,即指向信号处理函数

    //清空临时信号集,当信号处理函数结束时,信号集自动恢复
    sigemptyset(&act.sa_mask);

    //在临时信号集中屏蔽SIGQUIT信号,即在该信号处理函数中不会被SGIQUIT信号中断
    sigaddset(&act.sa_mask, SIGQUIT);

    act.sa_flags = 0;//调用哪一种,在上面的注释中有解释
/****************************************************/

/*****************3.注册sigaction结构体****************/
    //注册信号处理函数,当2号SIGINT信号到来时执行用户定义的信号处理函数
    sigaction(SIGINT, &act, NULL);
/****************************************************/

    while(1)
    {
        printf("************************\n");
        sleep(1);
    }
    return 0;
}



测试方法:
运行该程序,按下Ctrl+c发送SIGINT信号,此时会进入用户定义的信号处理函数
输出如下:
************************
************************
************************(按下Ctrl+c)
i am do_sig
n = 2n = 1n = 0num = 2
************************
************************
************************
************************(按下Ctrl+c)
i am do_sig
n = 2n = 1n = 0num = 2
************************
************************(按下Ctrl+\退出程序)

在开发过程中,我们往往不希望破坏linux原有的信号处理函数,所以内核也给我们提供了两个专门用于用户自定义的信号请看这一节中的SIGUSE1和SIGUSE2)10号信号SIGUSE1和12号信号SIGUSE2。
例子:

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

void do_sig(int num)
{
    printf("i am do_sig\n");
    printf("num = %d\n",num);
}

int main(void)
{
    struct sigaction act;
    act.sa_handler = do_sig;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT);
    act.sa_flags = 0;

    //注册信号处理函数
    sigaction(SIGUSR1, &act, NULL);

    while(1)
    {
        printf("************************\n");
        sleep(1);
    }
    return 0;
}


测试方法:
在一个终端中运行该程序(此时在该终端中会一直输出:********************)
在另一终端中输入ps -aux命令查询运行该程序的进程id号
并输入kill -10 进程id号   向该程序发送10号消息SIGUSR1
此时运行该程序的终端中会输出:i am do_sig
                          num = 10

另一方面,c库函数也为我们提供了一个封装更高层,接口更简单的函数signal

include <signal.h>

typedef void (*sighandler_t)(int)
sighandler_t signal(int signum, sighandler_t handler)

例子:

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

void do_sig(int n)
{
    printf("hello\n");
}

int main(void)
{
    signal(SIGINT, do_sig);

    while(1)
    {
        printf("****************************\n");
        sleep(1);
    }
    return 0;
}
运行后按Ctrl+c即可查看效果,若要关闭程序则按Ctrl+\
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值