C 为什么更改了默认的SIGPIPE处理程序?
我正在做一个测试,有人问我如何让“读”睡眠或“写”停止进程 对于后者,我不明白为什么我的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_
#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
$