全部的代码都已上传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);
}
}
}
然而使用标准的longjmp()函数从处理器函数中退出存在一个问题,在进入信号处理函数时,内核会自动的将引发调用的信号以及由act.sa_mask所指定的任意信号添加到进程的信号掩码中,并在处理器函数正常返回时将它们从掩码中清除,如果使用longjmp()退出信号处理函数将会引发? 由UNIX实现的血统决定Linux准许你System-V当信号处理函数从longjmp退出之后,不会将信号掩码回复,亦即在离开处理器函数时不会对阻塞的信号接触阻塞
/*************************************************************************\
* 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();
}
函数与abort()终止其调用进程,并生成核心转储
void abort(void);
int sigaltstack(const stack_t *ss, stack_t *oss);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
在备选栈中处理信号sigaltstack
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