尽我所能来解决这个问题,但我真的不想继续篡改我不完全理解的事情。因此,对于我必须在C中进行的编程分配,我需要在用户通过终端输入 CTRL + D 键击时终止程序。我试图在较小的测试函数中隔离该功能,但现在我的 CTRL + D 表现为我的 CTRL + C 和 CTRL + C 在完成执行时甚至在程序之外也没有任何效果。这是导致这种变化的程序:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
printf("\n");
signal(SIGINT, SIG_DFL);
exit(0);
}
int main(){
signal(SIGINT, ctrlD);
while(1) {
printf("Hello\n");
sleep(5);
}
}
在实现 CTRL + C 不再有效之后,添加了行signal(SIGINT, SIG_DFL);
。我认为它会将按键恢复到原来的功能,但无济于事。如何恢复原始功能,同时使 CTRL + D ?
***编辑:这个问题似乎已经过时了。我现在知道 Ctrl + D 不是信号。尽管如此,当我尝试在我的MAC OS终端中使用它时,我不再具有 Ctrl + C 的功能,而是 Ctrl + < kbd> D 似乎具有确切的功能。在我开始这个随意的旅程之前,我究竟能如何回归每个人以获得他们所拥有的功能?
答案 0 :(得分:2)
如果您打算在执行处理程序后恢复信号的默认行为,则在注册信号操作时将SA_RESETHAND标志传递给sa_flags。例如。
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);
来自sigaction()男人
SA_RESETHAND
在进入信号处理程序时将信号操作恢复为默认值。此标志仅在有意义时才有意义 建立信号处理程序。
答案 1 :(得分:0)
由于处理程序中的build
语句,当引发exit(0)
时,处理程序SIGINT
被调用,您可能会想到为什么strlD
不起作用?实际上它有效。但是您的主进程signal(SIGINT,SIG_DFL)
通过调用a.out
在那里成功终止。如果要恢复exit(0)
的行为,请删除exit(0)
。
SIGINT
同样建议使用#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
//printf("CTRL+C pressed\n");/* just to observe I added one printf
statement, Ideally there shouldn't be any printf here */
signal(SIGINT, SIG_DFL);/*restoring back to original action */
}
int main(){
signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */
while(1) {
printf("Hello\n");
sleep(5);
}
return 0;
}
而不是sigaction()
,如What is the difference between sigaction and signal?所述。阅读signal()
和man 2 sigaction
,查看man 2 exit
的含义。
此How to avoid using printf in a signal handler?
修改:
exit(0)
答案 2 :(得分:0)
如果您编写一个程序来探索信号,那么使用适当的POSIX接口(sigaction()
而不是signal()
)更好地编写 ,并避免未定义行为(在信号处理程序中使用非async-signal safe functions)。
例如,考虑以下程序:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
static volatile sig_atomic_t sigint_count = 0;
static void catch_sigint(int signum)
{
if (signum == SIGINT)
sigint_count++;
}
static int install_sigint(void)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sigint;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) == -1)
return errno;
return 0;
}
static int install_default(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
struct timespec duration;
int result;
if (install_sigint()) {
fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
duration.tv_sec = 5;
duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */
printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
fflush(stdout);
while (1) {
result = nanosleep(&duration, &duration);
if (!result)
break;
if (errno != EINTR) {
fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* nanosleep was interrupted by a delivery of a signal. */
if (sigint_count >= 3) {
/* Ctrl+C pressed three or more times. */
if (install_default(SIGINT) == -1) {
fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("SIGINT has been reverted to the default handler.\n");
fflush(stderr);
}
}
if (sigint_count > 0)
printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
else
printf("You did not press Ctrl+C at all.\n");
return EXIT_SUCCESS;
}
#define
告诉你的C库(特别是glibc)你想要它的POSIX.1-2008(及更高版本)功能。
INT
信号处理程序仅递增volatile sig_atomic_t
计数器。请注意,此类型可能具有非常小的范围; 0到127(包括0和127)应该是安全的。
主程序等待使用POSIX nanosleep()
功能。在某些系统上,sleep()
可以通过SIGALRM
函数实现,因此在使用信号时最好避免使用; nanosleep()
不会干扰这样的信号。另外,如果被信号传递中断,nanosleep()
可以返回剩余的时间量。
在主循环中,nanosleep()
将返回0,如果它已经睡眠整个间隔(但请注意,在这种情况下它可能不会将剩余时间更新为0)。如果它被信号传递中断,它将返回-1
errno == EINTR
,并更新剩余时间。 (第一个指针指向睡眠的持续时间,第二个指针指向应存储剩余时间的位置。您可以对两者使用相同的结构。)
通常,主循环只进行一次迭代。如果它被信号传递中断,它可以进行多次迭代。
当主循环检测到sigint_count
至少为3时,即它已收到至少三个INT
信号时,它会将信号处理程序重置为默认值。
(请注意,清除memset()
结构时,sigemptyset()
和struct sigaction
都很重要。memset()
可确保将来的代码向后兼容旧代码,确保偶数填充字段被清除。sigemptyset()
是清除信号掩码的安全方法(处理程序运行时阻塞的信号集)。)
(理论上,memset()
不是异步信号安全的,而sigemptyset()
和sigaction()
都是。这就是我在主程序中重置信号处理程序的原因,而不是在信号处理程序中。)
如果要从信号处理程序打印,则需要使用低级I / O,因为<stdio.h>
函数不是异步信号安全的。例如,您可以使用以下函数将字符串打印到标准输出:
static int wrerr(const char *p)
{
const int saved_errno = errno;
int retval = 0;
if (p) {
const char *q = p;
ssize_t n;
while (*q)
q++;
while (p < q) {
n = write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
}
errno = saved_errno;
return retval;
}
上面的wrerr()
函数是异步信号安全的(因为它本身只使用异步信号安全函数),它甚至保持errno
不变。 (许多指南忘记提到信号处理程序保持errno
不变是非常重要的。否则,当函数被信号处理程序中断,并且信号处理程序修改errno
时,原始函数将返回-1
以表示错误,但errno
不再是EINTR
!)
如果需要,您可以使用wrerr("INT signal!\n")
。如果写入成功,则wrerr()
的返回值为零,否则为错误代码。它忽略了中断本身。
请注意,不应将stderr
输出通过fprintf()
或其他<stdio.h>
函数与上述混合(除非程序中止时打印错误消息)。混合它们不是未定义的行为,它可能会产生令人惊讶的结果,例如wrerr()
输出中出现的fprintf(stderr,...)
输出。
答案 3 :(得分:0)
感谢所有为此问题做出贡献的人。在提供的其他丰富信息中,所提供/链接的资源对于更多地了解信号(以及EOF不是信号)非常有帮助。
经过一些更多的研究,我发现不知何故,无论是通过一些意外的bash命令出错,还是在我原来的问题中发布的程序,我都改变了我的终端设备的关键映射。如果有人在将来发现自己处于这种奇怪的特定情况,我希望这会有所帮助,因为它解决了我的问题:
输入命令$ stty -a
以查看所有终端设置,特别是&#34; cchars&#34;部分。
$ stty intr ^C
$ stty eof ^D
然后您可以再次运行$ stty -a
以查看更改是否已正常生效。再一次,谢谢大家。