我正在测试中,被问到如何使“读取”睡眠或“写入”停止进程”
对于后者,我不明白为什么我的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库的影响。
答案 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 demystified和termios(3)。
您还可以尝试通过将其stdin,stdout,stderr重定向到文件或管道来运行程序(也许使用stdout
或|& cat
和|& less
或{{1} }),或者不是tty的任何东西。
顺便说一句,Code::Blocks是free software。您可以研究其源代码以了解其功能。
还考虑使用stdbuf(1)通过不同的缓冲操作来运行程序。