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

C 为什么更改了默认的SIGPIPE处理程序?,c,pipe,signals,codeblocks,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_

我正在做一个测试,有人问我如何让“读”睡眠或“写”停止进程

对于后者,我不明白为什么我的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()
将具有预期的默认行为

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

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

回答

在与codeblocks的人交谈之后,codeblocks使用wxWidgets,在linux上(这里是Fedora28)wxWidgets使用gtk库,正如Mark Plotnick在评论中解释的那样,gtk更改了SIGPIPE的信号处理程序,因为代码块使用fork或exec运行代码,所以通过代码块运行的代码受gtk库的影响。

不是编译器,只是IDE。它(可能)运行编译器。GCC编译器对信号处理没有多大影响。读取和获取更多信息(禁止在信号处理程序内调用
printf
,因为
printf
不是异步信号安全的,所以信号处理程序内的
printf(“sigpipe received\n”);
是异步信号安全的)。信号处理主要由(请参阅上的源代码),其中一小部分可能由您的C标准库处理

似乎codeblocks更改了SIGPIPE的默认信号处理程序

这是非常不可能的(几乎可以肯定是错误的)。您可以在程序上使用它来了解系统调用它正在做什么

您忘记了一个
\n
。请记住
标准输出通常是行缓冲的。或者您应该调用

当您从内部运行程序时,Code::Blocks可以在没有终端的情况下运行。我强烈建议您在内部运行程序(请参阅和)。允许C标准库使用类似的东西,以便在stdout是或不是tty时(特别是,缓冲的方式不同,请参阅)。阅读和

您还可以尝试通过将其stdin、stdout、stderr重定向到文件或管道(可能使用
|&cat
|&less
bash
zsh
)或任何非tty的方法来运行程序

顺便说一句,你可以研究它的源代码来了解它在做什么


还可以考虑使用来运行具有不同缓冲操作的程序。

您报告的行为与Code::Blocks IDE设置一致,隐式或显式地将SIGPIPE行为传递给SIG\u IGN。这很容易被继承。这不是我所期望的-我期望您的程序使用SIGPIPE启动(事实上,所有其他信号)设置为SIG_DFL,这是默认的信号行为。如果这是问题所在,您就有理由向Code::Blocks的开发人员报告错误。如果这不是问题所在,那么我们需要认真思考,以确定实际发生的情况

您可以通过关注来自的返回值,或者通过在不修改信号处理模式的情况下使用来询问信号处理模式,来演示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()
),那么您将安装自己的信号处理程序。使用
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;
}
请注意,使用默认信号处理,程序在打印前终止。
123
。POSIX shells通过将退出状态报告为
128+N
,对“child死于信号N”进行编码;SIGPIPE为
13
,因此
141
表示shell死于SIGPIPE信号。(是的,现代年轻人可能会编写
(trap''PIPE;pipe83)
,而且它可以工作——当我学习shell编程时,这种细节是不可用的。)

对代码进行泛化以测试code::Blocks是否将任何其他信号设置为默认处理之外的信号并不难。但是,如果您想适应机器上可用的信号,这可能有点麻烦



*
在中,我们确定该程序在运行Fedora 28的VMware映像中运行,托管在Windows 10计算机上。因此,有足够多的地方可能会出现问题,即不清楚问题是否一定存在于code::Blocks中-根本不清楚问题源自何处。然而,问题似乎确实是,当测试程序从Code::Blocks运行时,测试程序是以SIGPIPE handling设置为SIG_IGN而不是SIG_DFL启动的。

Hmm,适用于我,Linux。平台是什么?它是否适用于此?如果忽略SIGPIPE,write将返回Epie。是这样吗?Fedora 28,以及epipe未返回:/
write()==epipe…
::write在出错时返回-1,并将errno设置为epipe。从命令行尝试。在gdb下运行代码块。结果发现gtk库在多个位置使进程忽略sigpipe。第一个是。注释说自2.18以来,
,gtk+调用信号(sigpipe,SIG\IGN)初始化期间,忽略SIGPIPE信号,因为图形应用程序中几乎不需要这些信号。如果出于某种原因确实需要处理SIGPIPE,请在gtk_init()之后重置处理程序,但请注意
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);
}
$ 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
$
#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
old handler was SIG_DFL
$ echo $?
141
$ (trap '' 13; pipe83)
old handler was SIG_IGN
EPIPE returned
123
$