c - 在 C 中处理 SIGTSTP 信号

标签 c signals

我在学习中遇到了这个示例代码:

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

char* nums[5] = {"One", "Two", "Three", "Four", "Five"};
char number[6];

void handler(int n) {
    printf(" %s\n", number);
}

int main() {
    signal(SIGTSTP, handler);      // ^Z
    for(int n = 0; ; n++) {
        strcpy(number, nums[n % 5]);
    }
}

显然,这段代码在编译和运行时有问题。尝试使用 Ctrl+Z 生成 SIGTSTP,而不是打印出“nums”中的一个单词然后退出,而是按预期打印出数字,但随后继续运行。它需要来自键盘的另一个 SIGTSTP 信号来真正让程序退出。

这是为什么?

最佳答案

为了回答您提出的两个实际问题,程序会继续运行,因为您的信号处理程序已返回,并且您在信号处理程序中编写的任何内容都不会导致程序终止,因此您的程序将继续正常运行。如果您希望程序终止,您需要从中调用 _exit()_Exit()

第二个 SIGTSTP 导致您的程序终止的原因是因为 signal() 是一个过时且不可靠的接口(interface),在类 Unix 平台上也不是标准接口(interface)。原始行为是,在使用 signal() 注册信号处理程序后,当实际传送该信号时,首先将该信号的配置重置为 SIG_DFL,然后 然后 您的信号处理程序运行。因此,在第一次传递该信号后,您的信号处理程序不再注册,除非您再次调用 signal()。因此,当信号第二次传递给您的程序时,不会调用您的信号处理程序,而是采取默认操作,在本例中是停止进程。此行为在 BSD 派生系统上有所不同,其中信号处理程序保持注册状态。所以实际上,您可以使用 signal() 做的唯一可靠的事情是将处置设置为 SIG_IGNSIG_DFL。对于其他任何事情,您应该使用带有 sigaction() 的现代可靠信号接口(interface)。

您的程序还有一些其他问题。首先,printf() 以及大多数标准库函数从信号处理程序调用是不安全的。 POSIX 定义 a complete list of asynchronous-signal-safe functions可以从信号处理程序中安全地调用它。相反,在这种情况下,您应该设置一个类型为 volatile sig_atomic_t 的标志,并将其设置在您的处理程序中。

其次,您在这里使用 strcpy() 的方式特别危险。假设 number 当前包含 “Two”。然后,在循环的下一次迭代中,它复制 'T''h''r' 'e',覆盖现有终止空字符的最后一个字符。 然后 发送信号并将此字符串传递给 printf()。即使没有从信号处理程序调用 printf() 的问题,您也向它发送一个 char 数组,该数组不以空字符结尾,您将招致未定义的行为和可能的段错误。

这是您的示例的一个更好的版本,使用 sigaction() 接口(interface),而不是从信号处理程序调用不安全的函数:

#define _POSIX_C_SOURCE 200809L

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

char * nums[] = {"One", "Two", "Three", "Four", "Five"};
volatile sig_atomic_t stop;

void handler(int n)
{
    stop = 1;
}

int main(void)
{
    struct sigaction sa;
    sa.sa_handler = handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);

    if ( sigaction(SIGTSTP, &sa, NULL) == -1 ) {
        perror("Couldn't set SIGTSTP handler");
        exit(EXIT_FAILURE);
    }

    int n = 0;
    while ( !stop ) {
        puts(nums[n]);
        n = (n + 1) % 5;
    }

    puts("Signal handler called, exiting.");

    return 0;
}

输出:

paul@horus:~/src/sandbox$ ./sig
One
Two
Three
Four
Five
One
^ZTwo
Three
Four
Five
One
Signal handler called, exiting.
paul@horus:~/src/sandbox$ 

您可以看到在输入 Ctrl-Z 和信号的实际传递之间存在延迟,这是完全正常的并且根据内核实际传递信号的方式可以预期。

关于c - 在 C 中处理 SIGTSTP 信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40098170/

相关文章:

c - 反转字符串的某些部分时遇到问题

c - 当数组不包含 null 终止符时,为什么 strlen() 返回奇怪的结果?

c - BeagleBone black 上 can 总线驱动程序的中断处理程序在哪里

c++ - 调用 MiniDumpWriteDump() 以捕获崩溃的最佳位置

matlab - 我可以使用什么MATLAB函数添加声音片段?

c - POSIX 是否保证信号不会传递给部分初始化的线程?

c - 使用 fork() 的进程树不均匀

c++ - 如何在MIPS汇编程序中使用库?

c - 为什么会有SIGFPE?

c - C中具有多个警报的多个线程