信号处理函数signal()和信号集函数组

分享到:
           

    本文关键字: 信号处理函数,signal(),信号集函数组

    信号处理的方法主要有两种,一种是使用signal()函数,另一种是使用信号集函数组。下面分别介绍这两种处理方式。

    1)使用signal()函数

    使用signal()函数处理时,只需指出要处理的信号和处理函数即可。它主要用于前32种非实时信号的处理,不支持信号传递信息,但是由于使用简单、易于理解,因此也受到很多程序员的欢迎。Linux还支持一个更健壮更新的信号处理函数sigaction(),推荐使用该函数。

    signal()函数的语法要点如表1所示。

表1 signal()函数语法要点

所需头文件 #include <signal.h>
函数原型 typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数传入值 signum:指定信号代码
handler SIG_IGN:忽略该信号
SIG_DFL:采用系统默认方式处理信号
自定义的信号处理函数指针
函数返回值 成功:以前的信号处理配置
出错:-1

    这里需要对该函数原型进行说明。这个函数原型有点复杂:首先该函数原型整体指向一个无返回值并且带一个整型参数的函数指针,也就是信号的原始配置函数;接着该原型又带有两个参数,其中第2个参数可以是用户自定义的信号处理函数的函数指针。

    表2列举了sigaction()函数的语法要点。

表2 sigaction()函数语法要点

所需头文件 #include <signal.h>
函数原型 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
函数传入值 signum:信号代码,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号
act:指向结构sigaction的一个实例的指针,指定对特定信号的处理
oldact:保存原来对相应信号的处理
函数返回值 成功:0
出错:-1

    这里要说明的是sigaction()函数中第2和第3个参数用到的sigaction结构,这是一个看似非常复杂的结构,希望读者能够慢慢阅读此段内容。

    首先给出了sigaction的定义,代码如下:

    struct sigaction
    {
        void (*sa_handler)(int signo);
        sigset_t sa_mask;
        int sa_flags;
        void (*sa_restore)(void);
    }

    sa_handler是一个函数指针,指定信号处理函数,这里除可以是用户自定义的处理函数外,还可以为SIG_DFL(采用默认的处理方式)或SIG_IGN(忽略信号)。它的处理函数只有一个参数,即信号值。

    sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被屏蔽,在调用信号捕获函数前,该信号集要加入到信号的信号屏蔽字中。

    sa_flags中包含了许多标志位,是对信号进行处理的各个选择项。它的常见可选值如表3所示。

表3 常见信号的含义及其默认操作

信 号 含 义
SA_NODEFER / SA_NOMASK 当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动屏蔽此信号
SA_NOCLDSTOP 进程忽略子进程产生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信号
SA_RESTART 令重启的系统调用起作用
SA_ONESHOT / SA_RESETHAND 自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作

    以下实例表明了如何使用signal()函数捕捉相应信号,并做出给定的处理。这里,my_func就是信号处理的函数指针,读者还可以将其改为SIG_IGN或SIG_DFL查看运行结果。第2个实例是用sigaction()函数实现同样的功能。

    以下是使用signal()函数的示例:

    /* signal.c */
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>

    /* 自定义信号处理函数 */
    void my_func(int sign_no)
    {
        if (sign_no == SIGINT)
        {
            printf("I have get SIGINT\n");
        }
        else if (sign_no == SIGQUIT)
        {
            printf("I have get SIGQUIT\n");
        }
    }

    int main()
    {
        printf("Waiting for signal SIGINT or SIGQUIT...\n");

        /* 发出相应的信号,并跳转到信号处理函数处 */
        signal(SIGINT, my_func);
        signal(SIGQUIT, my_func);
        pause();
        exit(0);
    }

    运行结果如下:

    $ ./signal
    Waiting for signal SIGINT or SIGQUIT...
    I have get SIGINT (按Ctrl+c 组合键)
    $ ./signal
    Waiting for signal SIGINT or SIGQUIT...
    I have get SIGQUIT (按Ctrl+\ 组合键)

    以下是用sigaction()函数实现同样的功能,只列出了更新的main()函数部分。

    /* sigaction.c */
    /* 前部分省略 */
    int main()
    {
        struct sigaction action;
        printf("Waiting for signal SIGINT or SIGQUIT...\n");

        /* sigaction结构初始化 */
        action.sa_handler = my_func;
        sigemptyset(&action.sa_mask);
        action.sa_flags = 0;

        /* 发出相应的信号,并跳转到信号处理函数处 */
        sigaction(SIGINT, &action, 0);
        sigaction(SIGQUIT, &action, 0);
        pause();
        exit(0);
    }

    2)信号集函数组

    使用信号集函数组处理信号时涉及一系列的函数,这些函数按照调用的先后次序可分为以下几大功能模块:创建信号集、注册信号处理函数及检测信号。

    其中,创建信号集主要用于处理用户感兴趣的一些信号,其函数包括以下几个。

    ● sigemptyset():将信号集初始化为空。

    ● sigfillset():将信号集初始化为包含所有已定义的信号集。

    ● sigaddset():将指定信号加入到信号集中。

    ● sigdelset():将指定信号从信号集中删除。

    ● sigismember():查询指定信号是否在信号集中。

    注册信号处理函数主要用于决定进程如何处理信号。这里要注意的是,信号集里的信号并不是真正可以处理的信号,只有当信号的状态处于非阻塞状态时才会真正起作用。因此,首先使用sigprocmask()函数检测并更改信号屏蔽字(信号屏蔽字是用来指定当前被阻塞的一组信号,它们不会被进程接收),然后使用sigaction()函数来定义进程接收到特定信号后的行为。

    检测信号是信号处理的后续步骤,因为被阻塞的信号不会传递给进程,所以这些信号就处于“未处理”状态(也就是进程不清楚它的存在)。sigpending()函数允许进程检测“未处理”信号,并进一步决定对它们做何处理。

    首先介绍创建信号集的函数格式,表4列举了这一组函数的语法要点。

表4 创建信号集函数语法要点

所需头文件 #include <signal.h>
函数原型 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(sigset_t *set, int signum)
函数传入值 set:信号集
signum:指定信号代码
函数返回值 成功:0(sigismember成功返回1,失败返回0)
出错:-1

    表5列举了sigprocmask()函数的语法要点。

表5 sigprocmask()函数语法要点

所需头文件 #include <signal.h>
函数原型 int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
函数传入值 how:决定函数的操作方式 SIG_BLOCK:增加一个信号集到当前进程的阻塞集中
SIG_UNBLOCK:从当前的阻塞集中删除一个信号集
SIG_SETMASK:将当前的信号集设置为信号阻塞集
set:指定信号集
oset:信号屏蔽字
函数返回值 成功:0
出错:-1

    此处,若set是一个非空指针,则参数how表示函数的操作方式;若how为空,则表示忽略此操作。

    表6列举了sigpending()函数的语法要点。

表6 sigpending()函数语法要点

所需头文件 #include <signal.h>
函数原型 int sigpending(sigset_t *set)
函数传入值 set:要检测的信号集
函数返回值 成功:0
出错:-1

    总之,在处理信号时,一般遵循如图1所示的操作流程。


图1 一般的信号操作处理流程

    本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》

   热点链接:

   1、信号捕捉函数alarm()和pause()
   2、信号发送函数kill()和raise()
   3、Linux下的信号机制
   4、有名管道(FIFO)
   5、标准流管道

更多新闻>>