问题描述
我在 Advanced Programming in the UNIX Environment 2nd ed. 的第一章中并使用 sigaction()
而不是 signal()
(和 )
作为提示而不是 {{ 1}}).
%
这实现了一个原始 shell,当接收到 #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
};
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,fmt,ap);
perror(buf);
}
static void mysigint(int signo) {
printf("Interrupt signal caught\n) ");
}
时,应该打印一个字符串然后继续。这本书使用了 SIGINT
,但手册页说要使用 signal()
,而且这本书很旧,所以我一开始选择了 sigaction()
。
当处理程序由 sigaction()
安装时向程序发送 SIGINT
时,处理程序会运行,但仍会退出 while 循环。 sigaction()
输出似乎表明 perror()
正在中断 SIGINT
内的 read()
?
fgets()
此外,处理程序的 $ ./a.out
) ^CInterrupt signal caught
Shell exit error: Interrupted system call
) $
中的最后一个 )
被延迟打印,直到 while 循环退出之后。
所以我尝试用对 printf()
的调用替换对 sigaction()
的调用。 signal()
与 mysigint
一起安装后,会发生这种情况:
signal()
所以 while 循环没有退出,但是处理程序中的右括号仍然被延迟打印,并且当它是三个而不是一个时打印。我认为在我按下回车之前,一个被存储在一个缓冲区中,第二个被存储在 while 循环的底部,然后在按下 Enter 并执行一个空命令后,这两个被打印出来加上第三个提示。但我不确定。
最后我回到了 $ ./a.out
) ^CInterrupt signal caught
<empty line,which I then type RETURN while on>
Couldn't execute: : No such file or directory
) ) )
但通过了 sigaction()
而不是 SIG_IGN
。它的表现与我预期的一样:
mysigint
我还尝试在 gdb 中测试 $ ./a.out
) ^C
和 sigaction()
变体,但它们在那里的表现不同。
我的问题是:我如何实现一个 signal()
处理程序,它的行为符合本书的要求?即打印“中断信号捕获”,然后立即打印新的shell提示。
如果它对我有帮助,我使用的是 Linux Mint 20。
解决方法
perror() 输出似乎表明 SIGINT 正在中断 fgets() 中的 read()
如果正在执行 read 系统调用,并且有信号到来,则 read 将返回错误并将 errno 设置为 EINTR。这适用于所有系统调用,或者至少适用于该块的系统调用。您可以将 fgets 更改为 read 并检查 read 给出的错误。如果 read 返回 -1 并且 errno 是 EINTR,则可以打印字符串,“Interrupt signal added\n)”。