linux_unix编程手册--信号处理函数

全部的代码都已上传github 地址:https://github.com/zzu-andrew/linux_unix

一般而言信号处理函数设计的越简单越好,其中最主要的原因就是降低引发竞争条件的风险。
更新全局变量或静态数据结构的函数可能是不可重入的。(只用到本地变量的函数肯定是可重入的),
将静态数据结构用于内部记账的函数也是不可重入的,其中最明显的例子就是stdio.h函数哭成员(printf()、scanf()等),他们会为缓冲区I/O更新内部数据结构。所以如果在信号处理函数中调用printf()函数又在主程序中又调用printf或者其他的stdio函数,就有可能出现奇怪的输出。
在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/2018120

/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2018.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 21-1 */

/* nonreentrant.c

   Demonstrate the nonreentrant nature of some library functions, in this
   example, crypt(3).
*/
#if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
#define _XOPEN_SOURCE 600
#endif
#include 
#include 
#include 
#include "tlpi_hdr.h"

static char *str2;              /* Set from argv[2] */
static int handled = 0;         /* Counts number of calls to handler */

static void
handler(int sig)
{
    crypt(str2, "xx");
    handled++;
}

int
main(int argc, char *argv[])
{
    char *cr1;
    int callNum, mismatch;
    struct sigaction sa;

    if (argc != 3)
        usageErr("%s str1 str2\n", argv[0]);

    str2 = argv[2];                      /* Make argv[2] available to handler */
    cr1 = strdup(crypt(argv[1], "xx"));  /* Copy statically allocated string
                                            to another buffer */
    if (cr1 == NULL)
        errExit("strdup");

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, NULL) == -1)
        errExit("sigaction");

    /* Repeatedly call crypt() using argv[1]. If interrupted by a
       signal handler, then the static storage returned by crypt()
       will be overwritten by the results of encrypting argv[2], and
       strcmp() will detect a mismatch with the value in 'cr1'. */
    /**
     * @brief  正常情况下,处理同一个字符得出的结果肯定是相同的,但是当捕捉到Crt+C之后就会出现不一样的结果,因为信号处理函数中断了加密函数的执行
     * @note   因此一旦捕捉到信号就会,进入到if判断条件,出现打印的结果
     * @param  1: 
     * @param  callNum++: 
     * @retval 
     */
    for (callNum = 1, mismatch = 0; ; callNum++) {
        if (strcmp(crypt(argv[1], "xx"), cr1) != 0) {
            mismatch++;
            printf("Mismatch on call %d (mismatch=%d handled=%d)\n",
                    callNum, mismatch, handled);
        }
    }
}

在这里插入图片描述

linux_unix编程手册--信号处理函数_第1张图片

在这里插入图片描述
在这里插入图片描述

然而使用标准的longjmp()函数从处理器函数中退出存在一个问题,在进入信号处理函数时,内核会自动的将引发调用的信号以及由act.sa_mask所指定的任意信号添加到进程的信号掩码中,并在处理器函数正常返回时将它们从掩码中清除,如果使用longjmp()退出信号处理函数将会引发?   由UNIX实现的血统决定Linux准许你System-V当信号处理函数从longjmp退出之后,不会将信号掩码回复,亦即在离开处理器函数时不会对阻塞的信号接触阻塞
linux_unix编程手册--信号处理函数_第2张图片
在这里插入图片描述

/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2018.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 21-2 */

/* sigmask_longjmp.c

   Demonstrate the different effects of longjmp() and siglongjmp()
   on the process signal mask.

   By default, this program uses setjmp() + longjmp(). Compile with
   -DUSE_SIGSETJMP to use sigsetjmp() + siglongjmp().
*/

#define _GNU_SOURCE     /* Get strsignal() declaration from  */
#include 
#include 
#include 
#include "signal_functions.h"           /* Declaration of printSigMask() */
#include "tlpi_hdr.h"

static volatile sig_atomic_t canJump = 0;
                        /* Set to 1 once "env" buffer has been
                           initialized by [sig]setjmp() */
#ifdef USE_SIGSETJMP
static sigjmp_buf senv;
#else
static jmp_buf env;
#endif

static void
handler(int sig)
{
    /* UNSAFE: This handler uses non-async-signal-safe functions
       (printf(), strsignal(), printSigMask(); see Section 21.1.2) */

    printf("Received signal %d (%s), signal mask is:\n", sig,
            strsignal(sig));
    printSigMask(stdout, NULL);

    if (!canJump) {
        printf("'env' buffer not yet set, doing a simple return\n");
        return;
    }

#ifdef USE_SIGSETJMP
    siglongjmp(senv, 1);
#else
    longjmp(env, 1);
#endif
}

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

    printSigMask(stdout, "Signal mask at startup:\n");

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, NULL) == -1)
        errExit("sigaction");

#ifdef USE_SIGSETJMP
    printf("Calling sigsetjmp()\n");
    if (sigsetjmp(senv, 1) == 0)
#else
    printf("Calling setjmp()\n");
    if (setjmp(env) == 0)
#endif
        canJump = 1;                    /* Executed after [sig]setjmp() */

    else                                /* Executed after [sig]longjmp() */
        printSigMask(stdout, "After jump from handler, signal mask is:\n" );

    for (;;)                            /* Wait for signals until killed */
        pause();
}

若是定义了USE_SIGSETJMP 宏
linux_unix编程手册--信号处理函数_第3张图片

异常终止进程:abort()

函数与abort()终止其调用进程,并生成核心转储
void abort(void);
linux_unix编程手册--信号处理函数_第4张图片


       int sigaltstack(const stack_t *ss, stack_t *oss);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

在备选栈中处理信号sigaltstack
linux_unix编程手册--信号处理函数_第5张图片
sigaltstack函数的作用就是即使函数的栈溢出也能够调用信号处理函数

/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2018.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 21-3 */

/* t_sigaltstack.c

   Demonstrate the use of sigaltstack() to handle a signal on an alternate
   signal stack.
*/
#define _GNU_SOURCE             /* Get strsignal() declaration from  */
#include 
#include 
#include "tlpi_hdr.h"

static void
sigsegvHandler(int sig)
{
    int x;

    /* UNSAFE: This handler uses non-async-signal-safe functions
       (printf(), strsignal(), fflush(); see Section 21.1.2) */

    printf("Caught signal %d (%s)\n", sig, strsignal(sig));
    printf("Top of handler stack near     %10p\n", (void *) &x);
    fflush(NULL);

    _exit(EXIT_FAILURE);                /* Can't return after SIGSEGV */
}

/* The following stops 'gcc -Wall' complaining that "control reaches
   end of non-void function" because we don't follow the call to
   overflowStack() stack in main() with a call to exit(). */

#ifdef __GNUC__
static void
overflowStack(int callNum) __attribute__ ((__noreturn__));
#endif

static void             /* A recursive function that overflows the stack */
overflowStack(int callNum)  //连续调用知道程序的栈溢出
{
    char a[100000];                     /* Make this stack frame large */

    printf("Call %4d - top of stack near %10p\n", callNum, &a[0]);
    overflowStack(callNum+1);
}

int
main(int argc, char *argv[])
{
    stack_t sigstack;
    struct sigaction sa;
    int j;

    printf("Top of standard stack is near %10p\n", (void *) &j);

    /* Allocate alternate stack and inform kernel of its existence */

    sigstack.ss_sp = malloc(SIGSTKSZ);
    if (sigstack.ss_sp == NULL)
        errExit("malloc");

    sigstack.ss_size = SIGSTKSZ;
    sigstack.ss_flags = 0;
    if (sigaltstack(&sigstack, NULL) == -1)
        errExit("sigaltstack");
    printf("Alternate stack is at         %10p-%p\n",
            sigstack.ss_sp, (char *) sbrk(0) - 1);

    sa.sa_handler = sigsegvHandler;     /* Establish handler for SIGSEGV */
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;           /* Handler uses alternate stack */
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
        errExit("sigaction");

    overflowStack(1);
}

可见上述函数就算栈溢出,信号处理器也能够正常的执行

$ ./t_sigaltstack
Top of standard stack is near 0x7ffd2c51877c
Alternate stack is at          0x1f83420-0x1fa3fff
Call    1 - top of stack near 0x7ffd2c5000a0
Call    2 - top of stack near 0x7ffd2c4e79d0
Call    3 - top of stack near 0x7ffd2c4cf300
Call    4 - top of stack near 0x7ffd2c4b6c30
Call    5 - top of stack near 0x7ffd2c49e560
.
.
.
Call   77 - top of stack near 0x7ffd2bdbfae0
Call   78 - top of stack near 0x7ffd2bda7410
Call   79 - top of stack near 0x7ffd2bd8ed40
Call   80 - top of stack near 0x7ffd2bd76670
Call   81 - top of stack near 0x7ffd2bd5dfa0
Call   82 - top of stack near 0x7ffd2bd458d0
Call   83 - top of stack near 0x7ffd2bd2d200
Caught signal 11 (Segmentation fault)
Top of handler stack near      0x1f84ee4  
andrew@andrew-Thurley:/work/svn_linux/linux_unix/sys

你可能感兴趣的:(linux)