如何在C中恢复原始信号处理属性

时间:2018-04-18 04:14:03

标签: c signals

尽我所能来解决这个问题,但我真的不想继续篡改我不完全理解的事情。因此,对于我必须在C中进行的编程分配,我需要在用户通过终端输入 CTRL + D 键击时终止程序。我试图在较小的测试函数中隔离该功能,但现在我的 CTRL + D 表现为我的 CTRL + C CTRL + C 在完成执行时甚至在程序之外也没有任何效果。这是导致这种变化的程序:

#include <unistd.h> 
#include <stdio.h> 
#include <termios.h> 
#include <signal.h> 
#include <stdlib.h>

void ctrlD(int sig){ 
  printf("\n");
  signal(SIGINT, SIG_DFL);
  exit(0); 
}

int main(){
  signal(SIGINT, ctrlD);
  while(1) {
    printf("Hello\n");
    sleep(5);
  }
}

在实现 CTRL + C 不再有效之后,添加了行signal(SIGINT, SIG_DFL);。我认为它会将按键恢复到原来的功能,但无济于事。如何恢复原始功能,同时使 CTRL + D

***编辑:这个问题似乎已经过时了。我现在知道 Ctrl + D 不是信号。尽管如此,当我尝试在我的MAC OS终端中使用它时,我不再具有 Ctrl + C 的功能,而是 Ctrl + < kbd> D 似乎具有确切的功能。在我开始这个随意的旅程之前,我究竟能如何回归每个人以获得他们所拥有的功能?

4 个答案:

答案 0 :(得分:2)

如果您打算在执行处理程序后恢复信号的默认行为,则在注册信号操作时将SA_RESETHAND标志传递给sa_flags。例如。

struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);

来自sigaction()男人

  

SA_RESETHAND

     

在进入信号处理程序时将信号操作恢复为默认值。此标志仅在有意义时才有意义   建立信号处理程序。

答案 1 :(得分:0)

由于处理程序中的build语句,当引发exit(0)时,处理程序SIGINT被调用,您可能会想到为什么strlD不起作用?实际上它有效。但是您的主进程signal(SIGINT,SIG_DFL)通过调用a.out在那里成功终止。如果要恢复exit(0)的行为,请删除exit(0)

SIGINT

同样建议使用#include <unistd.h> #include <stdio.h> #include <termios.h> #include <signal.h> #include <stdlib.h> void ctrlD(int sig){ //printf("CTRL+C pressed\n");/* just to observe I added one printf statement, Ideally there shouldn't be any printf here */ signal(SIGINT, SIG_DFL);/*restoring back to original action */ } int main(){ signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */ while(1) { printf("Hello\n"); sleep(5); } return 0; } 而不是sigaction(),如What is the difference between sigaction and signal?所述。阅读signal()man 2 sigaction,查看man 2 exit的含义。

How to avoid using printf in a signal handler?

修改

exit(0)

答案 2 :(得分:0)

如果您编写一个程序来探索信号,那么使用适当的POSIX接口(sigaction()而不是signal())更好地编写 ,并避免未定义行为(在信号处理程序中使用非async-signal safe functions)。

例如,考虑以下程序:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>

static volatile sig_atomic_t  sigint_count = 0;

static void catch_sigint(int signum)
{
    if (signum == SIGINT)
        sigint_count++;
}

static int install_sigint(void)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);

    act.sa_handler = catch_sigint;
    act.sa_flags = 0;

    if (sigaction(SIGINT, &act, NULL) == -1)
        return errno;

    return 0;
}

static int install_default(const int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);

    act.sa_handler = SIG_DFL;
    act.sa_flags = 0;

    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

int main(void)
{
    struct timespec  duration;
    int              result;

    if (install_sigint()) {
        fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    duration.tv_sec = 5;
    duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */

    printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
    fflush(stdout);
    while (1) {

        result = nanosleep(&duration, &duration);
        if (!result)
            break;

        if (errno != EINTR) {
            fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        /* nanosleep was interrupted by a delivery of a signal. */

        if (sigint_count >= 3) {
            /* Ctrl+C pressed three or more times. */

            if (install_default(SIGINT) == -1) {
                fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
                return EXIT_FAILURE;
            }

            printf("SIGINT has been reverted to the default handler.\n");
            fflush(stderr);
        }
    }

    if (sigint_count > 0)
        printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
    else
        printf("You did not press Ctrl+C at all.\n");

    return EXIT_SUCCESS;
}

#define告诉你的C库(特别是glibc)你想要它的POSIX.1-2008(及更高版本)功能。

INT信号处理程序仅递增volatile sig_atomic_t计数器。请注意,此类型可能具有非常小的范围; 0到127(包括0和127)应该是安全的。

主程序等待使用POSIX nanosleep()功能。在某些系统上,sleep()可以通过SIGALRM函数实现,因此在使用信号时最好避免使用; nanosleep()不会干扰这样的信号。另外,如果被信号传递中断,nanosleep()可以返回剩余的时间量。

在主循环中,nanosleep()将返回0,如果它已经睡眠整个间隔(但请注意,在这种情况下它可能不会将剩余时间更新为0)。如果它被信号传递中断,它将返回-1 errno == EINTR,并更新剩余时间。 (第一个指针指向睡眠的持续时间,第二个指针指向应存储剩余时间的位置。您可以对两者使用相同的结构。)

通常,主循环只进行一次迭代。如果它被信号传递中断,它可以进行多次迭代。

当主循环检测到sigint_count至少为3时,即它已收到至少三个INT信号时,它会将信号处理程序重置为默认值。

(请注意,清除memset()结构时,sigemptyset()struct sigaction都很重要。memset()可确保将来的代码向后兼容旧代码,确保偶数填充字段被清除。sigemptyset()是清除信号掩码的安全方法(处理程序运行时阻塞的信号集)。)

(理论上,memset()不是异步信号安全的,而sigemptyset()sigaction()都是。这就是我在主程序中重置信号处理程序的原因,而不是在信号处理程序中。)

如果要从信号处理程序打印,则需要使用低级I / O,因为<stdio.h>函数不是异步信号安全的。例如,您可以使用以下函数将字符串打印到标准输出:

static int wrerr(const char *p)
{
    const int  saved_errno = errno;
    int        retval = 0;

    if (p) {
        const char *q = p;
        ssize_t     n;

        while (*q)
            q++;

        while (p < q) {
            n = write(STDERR_FILENO, p, (size_t)(q - p));
            if (n > 0)
                p += n;
            else
            if (n != -1) {
                retval = EIO;
                break;
            } else
            if (errno != EINTR) {
                retval = errno;
                break;
            }
        }
    }

    errno = saved_errno;
    return retval;
}

上面的wrerr()函数是异步信号安全的(因为它本身只使用异步信号安全函数),它甚至保持errno不变。 (许多指南忘记提到信号处理程序保持errno不变是非常重要的。否则,当函数被信号处理程序中断,并且信号处理程序修改errno时,原始函数将返回-1以表示错误,但errno不再是EINTR!)

如果需要,您可以使用wrerr("INT signal!\n")。如果写入成功,则wrerr()的返回值为零,否则为错误代码。它忽略了中断本身。

请注意,不应将stderr输出通过fprintf()或其他<stdio.h>函数与上述混合(除非程序中止时打印错误消息)。混合它们不是未定义的行为,它可能会产生令人惊讶的结果,例如wrerr()输出中出现的fprintf(stderr,...)输出。

答案 3 :(得分:0)

感谢所有为此问题做出贡献的人。在提供的其他丰富信息中,所提供/链接的资源对于更多地了解信号(以及EOF不是信号)非常有帮助。

经过一些更多的研究,我发现不知何故,无论是通过一些意外的bash命令出错,还是在我原来的问题中发布的程序,我都改变了我的终端设备的关键映射。如果有人在将来发现自己处于这种奇怪的特定情况,我希望这会有所帮助,因为它解决了我的问题:

输入命令$ stty -a以查看所有终端设置,特别是&#34; cchars&#34;部分。

然后我看到了逆转,并修复了它:

$ stty intr ^C
$ stty eof ^D

然后您可以再次运行$ stty -a以查看更改是否已正常生效。再一次,谢谢大家。