为什么更改了默认SIGPIPE处理程序?

时间:2018-12-16 12:02:09

标签: c pipe signals codeblocks

我正在测试中,被问到如何使“读取”睡眠或“写入”停止进程”

对于后者,我不明白为什么我的sigpipe确实升高了,但没有停止该过程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

void signal_handler(int signal){
    printf("sigpipe received\n");
}

int main(void)
{
    int tube[DESCRIPTOR_COUNT];
    pipe(tube);
//    signal(SIGPIPE, signal_handler);
    close(tube[READING]);

    if(write(tube[WRITING], "message", 8)<0)
        if(errno==EPIPE)
            printf("EPIPE returned\n");

    printf("123");

    return EXIT_SUCCESS;
}

没有signal()(引用)

带有signal()(未引用)

确实收到了SIGPIPE,但是在我不处理的情况下,该过程应停止,但是由于我可以写“ 123”,这意味着该过程没有停止< / em>。 为什么?

我也在Fedora 28上,我在使用代码块17.12。

SIGPIPE被忽略了吗?原因吗?

解决方案?

struct sigaction action;
action.sa_handler = SIG_DFL;
sigaction(SIGPIPE, &action, 0);

以此替换signal()将具有默认行为!

编辑,我现在将标题从“ SIGPIPE不会停止进程”更改为“为什么更改了默认SIGPIPE处理程序?”

================================================ =======

答案

与代码块中的人交谈之后,代码块使用wxWidgets,在linux(此处为fedora 28)上,wxWidgets使用gtk库,如Mark Plotnick在评论中所述,由于代码块使用以下代码运行代码,因此gtk更改了SIGPIPE的信号处理程序。叉子或执行程序,通过代码块运行的代码受gtk库的影响。

2 个答案:

答案 0 :(得分:3)

您要报告的行为与Code :: Blocks IDE设置隐式或显式地将SIGPIPE行为与SIG_IGN一致。这很容易继承。这不是我期望的-我希望您的程序在SIGPIPE(实际上是所有其他信号)设置为SIG_DFL(默认信号行为)的情况下启动。如果这确实是问题所在,则您有必要向Code :: Blocks开发人员报告错误。如果事实证明不是问题所在,那么我们就需要思考一些困难,以找出实际发生的情况 *

您可以通过关注signal()的返回值,或使用sigaction()来查询信号处理模式而不修改它,来演示Code :: Blocks是否正在发生这种情况。

例如:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

static void signal_handler(int signum)
{
    // Lazy; normally, I'd format the signal number into the string, carefully
    (void)signum;
    write(STDOUT_FILENO, "sigpipe received\n", sizeof("sigpipe received\n")-1);
}

int main(void)
{
    void (*handler)(int) = signal(SIGPIPE, signal_handler);

    if (handler == SIG_DFL)
        printf("old handler was SIG_DFL\n");
    else if (handler == SIG_IGN)
    {
        printf("old handler was SIG_IGN\n");
        (void)signal(SIGPIPE, SIG_IGN);
    }
    else
    {
        // Standard C does not allow a cast from function pointer to object pointer
        //printf("there was a non-standard handler installed (%p)\n", (void *)handler);
        printf("there was a non-standard handler installed\n");
    }

    int tube[DESCRIPTOR_COUNT];
    pipe(tube);    
    close(tube[READING]);

    if (write(tube[WRITING], "message", 8) < 0)
    {
        if (errno == EPIPE)
            printf("EPIPE returned\n");
        else
            printf("errno = %d\n", errno);
    }

    printf("123\n");

    return EXIT_SUCCESS;
}

请注意,在程序中使用signal()设置信号处理程序的标准习惯用法是:

if (signal(signum, SIG_IGN) != SIG_IGN)
    signal(signum, signal_handler);

这意味着,如果某个程序受到信号保护,则它将保持受保护状态。如果它正在处理信号(默认情况下,或者可能是通过先前对signal()的调用来显式处理的),那么您将安装自己的信号处理程序。使用sigaction()的等效代码为:

struct sigaction sa;
if (sigaction(signum, 0, &sa) == 0 && sa.sa_handler != SIG_IGN)
{
    sa.sa_handler = signal_handler;
    sa.sa_flag &= ~SA_SIGINFO;
    sigaction(signum, sa, 0);
}

(此方法的一个优点:通过调用sigaction()来初始化结构,因此无需摆弄掩码。对标志的调整可确保基本处理程序(而不是扩展处理程序)是

当我将源代码(pipe13.c)编译到程序pipe13中并运行它时,我得到:

$ make pipe13
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe13.c -o pipe13
$ pipe13
old handler was SIG_DFL
sigpipe received
EPIPE returned
123
$ (trap '' 13; pipe13)
old handler was SIG_IGN
EPIPE returned
123
$

此变体使用sigaction()询问信号处理:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

int main(void)
{
    struct sigaction sa;
    if (sigaction(SIGPIPE, 0, &sa) != 0)
        fprintf(stderr, "siagaction() failed\n");
    else if (sa.sa_handler == SIG_DFL)
        printf("old handler was SIG_DFL\n");
    else if (sa.sa_handler == SIG_IGN)
        printf("old handler was SIG_IGN\n");
    else
        printf("there was a non-standard handler installed\n");

    int tube[DESCRIPTOR_COUNT];
    pipe(tube);    
    close(tube[READING]);

    if (write(tube[WRITING], "message", 8) < 0)
    {
        if (errno == EPIPE)
            printf("EPIPE returned\n");
        else
            printf("errno = %d\n", errno);
    }

    printf("123\n");

    return EXIT_SUCCESS;
}

运行(程序pipe83)时,我得到:

$ pipe83
old handler was SIG_DFL
$ echo $?
141
$ (trap '' 13; pipe83)
old handler was SIG_IGN
EPIPE returned
123
$

请注意,使用默认信号处理,程序在打印123之前终止。 POSIX外壳通过将退出状态报告为128 + N来编码“子死于信号N”; SIGPIPE是13,因此141表示外壳程序因SIGPIPE信号而死亡。 (是的,现代的年轻人可能会写(trap '' PIPE; pipe83)并且行得通—当我学习shell编程时,这些细微之处就无法得到。)

概括代码以测试Code :: Blocks是否将其他信号设置为默认处理方式并非难事。但是,如果您想适应机器上可用的信号,可能会有些麻烦。


* chat中,我们确定程序在运行Windows 10计算机上运行的Fedora 28的VMware映像中运行。因此,有足够的可能发生麻烦的地方是,不清楚问题是否一定存在于Code :: Blocks中—根本不清楚问题的根源。但是,确实确实存在问题,当从Code :: Blocks运行该测试程序时,它会将SIGPIPE处理设置为SIG_IGN而不是SIG_DFL。

答案 1 :(得分:0)

Code::Blocks不是编译器,只是IDE。它(可能)运行GCC编译器。而且,GCC编译器对于信号处理并不重要。阅读signal(7)signal-safety(7)了解更多信息(禁止在信号处理程序中调用printf,因为printf不是异步信号安全,因此您的{信号处理程序中的{1}}是undefined behavior)。信号处理主要由Linux kernel(请参见kernel.org的源代码)完成,其中一小部分由C标准库(可能为GNU glibc)处理。

  

似乎代码块更改了SIGPIPE的默认信号处理程序

这不太可能(而且几乎肯定是错误的)。您可以在程序上使用strace(1)来了解系统正在执行的操作。

printf("sigpipe received\n");

您忘记了 printf("123"); 。请记住,\n通常是行缓冲的。或者您应该致电fflush(3)

当您从Code :: Block内部运行程序时,它可以在没有终端的情况下运行。我强烈建议您在terminal emulator内运行程序(请参见tty(4)pty(7))。当stdout是tty或不是tty时,允许C标准库使用类似isatty(3)的行为(特别是缓冲以不同的方式进行,请参见setvbuf(3))。阅读tty demystifiedtermios(3)

您还可以尝试通过将其stdin,stdout,stderr重定向到文件或管道来运行程序(也许使用stdout|& cat|& less或{{1} }),或者不是tty的任何东西。

顺便说一句,Code::Blocksfree software。您可以研究其源代码以了解其功能。

还考虑使用stdbuf(1)通过不同的缓冲操作来运行程序。