使用信号和分叉做家庭作业并且信号有问题。
我创建了这个函数:
void trata_sinal_int() {
char op[2];
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y') {
printf("Bye Bye\n");
exit(0);
}
}
主要是我:
signal(SIGINT, trata_sinal_int);
运行时,按CTRL ^C
调用函数void trata_sinal_int()
,我收到了消息。
如果按y
程序按预期结束,但如果我按n
程序仍然结束。
在按CTRL ^C
之前,他没有回来。
这应该发生吗?
答案 0 :(得分:5)
这取决于您所遵循的标准,但标准C不允许您做更多的事情而不是修改volatile sig_atomic_t
类型的变量或致电_Exit
(或abort()
或来自信号处理程序的signal()
)。 POSIX更宽松。您的信号处理程序中的代码充满了用户交互,正在超越POSIX允许的范围。通常,您希望信号处理函数小而且细长。
请注意,信号处理函数应为:
void trata_sinal_int(int signum)
{
这允许您在没有关于类型不匹配的强制转换或编译器警告的情况下进行编译。
signal()
函数可以在调用信号处理程序时将其重置为默认行为;经典地说,有必要在信号处理程序中恢复信号处理程序:
signal(signum, trata_sinal_int);
到目前为止,这一切都非常普遍和半平凡。
当您键入 Control-C 时,系统会返回到最初收到信号时的大致位置。但是,接下来发生的事情取决于它的位置(在处理程序中必须非常小心的原因之一)。例如,如果它正在操纵malloc()
内的自由列表指针,它会返回那里,但是如果你在处理程序中重新调用malloc()
,那么所有的地狱可能都会崩溃。如果您在系统调用中,那么您的呼叫可能会被中断(返回错误指示和errno == EINTR
),或者它可能会从停止的位置恢复。否则,它应该返回计算运行的位置。
这是(在修复版本中)您的代码内置到测试装备中。 pause()
函数在返回之前等待信号。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
static void trata_sinal_int(int signum)
{
char op[2];
signal(signum, trata_sinal_int);
printf("\nTerminate? (y/n)\n");
scanf("%s", op);
if (op[0] == 'y')
{
printf("Bye Bye\n");
exit(0);
}
}
int main(void)
{
signal(SIGINT, trata_sinal_int);
for (int i = 0; i < 3; i++)
{
printf("Pausing\n");
pause();
printf("Continuing\n");
}
printf("Exiting\n");
return(0);
}
我应该指出scanf()
根本不是很安全;大小为2的缓冲区是缓冲区溢出的公开邀请。我也没有错误检查系统调用。
我在Mac OS X 10.7.5上测试过BSD衍生产品。很有可能在这个平台上重置signal()
是不必要的,因为BSD在很久以前(POSIX之前)引入了“可靠信号”。
signal
函数¶5如果信号的出现不是调用
abort
或raise
函数的结果, 如果信号处理程序引用具有静态或线程的任何对象,则行为未定义 存储持续时间不是无锁原子对象,而是通过为其分配值 声明为volatile sig_atomic_t
的对象,或者信号处理程序调用任何函数 在abort
函数以外的标准库中,_Exit
函数,quick_exit
函数,或signal
函数,第一个参数等于 信号编号对应于导致调用处理程序的信号。 此外,如果对signal
函数的此类调用导致SIG_ERR
返回,则 errno的值是不确定的。 252)252)如果异步信号处理程序生成任何信号,则行为未定义。
对quick_exit()
的引用是C2011中的新内容;它们不在C1999中。
关于Signal Concepts的部分详细介绍了POSIX下信号处理程序内部和不允许的内容。
答案 1 :(得分:2)
首先,您的信号处理程序并非完全异步信号安全。在实践中,这可能不是你的问题,因为我假设main()在等待信号时基本上什么都不做。但无论如何它绝对不正确。
至于为什么程序退出,不计算由于无效使用FILE *函数(如printf,sscanf等)而导致信号处理程序中的段错误:当收到信号时,您正在进行任何系统调用(或者,大多数)将与EAGAIN
中断。
如果您在main中使用类似sleep()
的内容来等待信号发生,它将被中断。您需要手动重新启动它。
为避免这种情况,您可能希望使用明显更具可移植性的sigaction
界面而不是signal
。如果没有别的,这允许您指示您希望重新启动系统调用。
信号处理程序中不允许that FILE *
函数(以及使用全局状态的大多数其他函数,例如malloc
和free
)的原因是您可能处于另一个函数的中间信号到达时在相同状态下操作。
这可能会导致段错误或其他未定义的操作。
实现这一点的正常'技巧'是拥有一个自管道:信号处理程序将一个字节写入管道,你的主循环将看到这一点(通常通过等待poll
或类似的东西然后采取行动。
如果您绝对想在信号处理程序中进行用户交互,则必须使用write()
和read()
,而不是FILE*
函数。