【发布时间】:2020-12-25 06:36:47
【问题描述】:
我在Advanced Programming in the UNIX Environment 2nd ed.的第一章,并使用sigaction()而不是signal()(和)作为提示符而不是%)实现了图1.10中的程序。
#include<stdio.h>
#include<errno.h>
#include<signal.h>
#include<stdarg.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#define MAXLINE 1024
static void mysigint(int signo);
struct sigaction mysigaction = {
mysigint,
0,
0,
0,
0
};
static void err_ret(const char *fmt, ...);
int main(void) {
char buf[MAXLINE];
pid_t pid;
int status;
if (sigaction(SIGINT, &mysigaction, NULL) < 0) {
err_ret("Signal error");
exit(1);
}
// signal(SIGINT, mysigint);
printf(") ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = 0;
if ((pid = fork()) < 0) {
perror("Fork error");
exit(1);
} else if (pid == 0) { // Child
execlp(buf, buf, (char *)0);
err_ret("Couldn't execute: %s",buf);
exit(127);
}
if ((pid = waitpid(pid, &status, 0)) < 0) { // Parent
perror("Waitpid error");
exit(1);
}
printf(") ");
}
if (errno)
perror("Shell exit error");
else
printf("Exiting shell...\n");
exit(0);
}
static void err_ret(const char *fmt, ...) {
va_list ap;
char buf[MAXLINE];
va_start(ap, fmt);
vsnprintf(buf, MAXLINE, fmt, ap);
perror(buf);
}
static void mysigint(int signo) {
printf("Interrupt signal caught\n) ");
}
这实现了一个原始 shell,当接收到SIGINT 时,应该打印一个字符串然后继续。这本书使用signal(),但手册页说要使用sigaction(),而且这本书很旧,所以我一开始就使用sigaction()。
当sigaction() 安装了处理程序时,向程序发送SIGINT 时,处理程序会运行,但仍会退出while 循环。 perror() 输出似乎表明 SIGINT 正在中断 read() 内部 fgets()?
$ ./a.out
) ^CInterrupt signal caught
Shell exit error: Interrupted system call
) $
此外,处理程序的printf() 中的最终) 被延迟打印,直到while 循环退出之后。
所以我尝试将对sigaction() 的调用替换为对signal() 的调用。安装了mysigint 和signal(),会发生这种情况:
$ ./a.out
) ^CInterrupt signal caught
<empty line, which I then type RETURN while on>
Couldn't execute: : No such file or directory
) ) )
所以 while 循环没有退出,但处理程序中的右括号仍然延迟打印,当它是三个而不是一个时打印。我认为在我按回车之前,一个存储在缓冲区中,第二个存储在 while 循环的底部,然后在按回车键并执行空命令后打印这两个以及第三个提示。但我不确定。
最后我回到了sigaction(),但通过了SIG_IGN而不是mysigint。它的行为与我预期的一样:
$ ./a.out
) ^C
我还尝试在 gdb 中测试 sigaction() 和 signal() 变体,但它们在那里的行为不同。
我的问题是:我如何实现一个行为符合本书要求的SIGINT 处理程序?也就是打印“Interrupt signal caught”,然后立即打印一个新的 shell 提示符。
如果它对我使用 Linux Mint 20 有帮助。
【问题讨论】:
-
在信号处理程序中使用
printf()会导致未定义的行为。 -
阅读signal(7) 和signal-safety(7)。考虑将signalfd(2) 与poll(2) 一起使用。注意sigsetjmp(3)
-
@BasileStarynkevitch 谢谢,我去看看那些资源
-
很多时候,信号处理程序只是设置一些
volatile sigatomic_t标志。学习GNU bash源代码,求灵感