【问题标题】:Implementing a SIGINT handler in a fgets() loop在 fgets() 循环中实现 SIGINT 处理程序
【发布时间】: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() 的调用。安装了mysigintsignal(),会发生这种情况:

$ ./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源代码,求灵感

标签: c linux signals


【解决方案1】:

perror() 输出似乎表明 SIGINT 正在中断 fgets() 中的 read()

如果正在执行 read 系统调用,并且有信号到来,则 read 将返回错误并设置 errno 为 EINTR。对于所有系统调用都是如此,或者至少是系统调用该块。您可以将 fgets 更改为 read 并检查 read 给出的错误。如果 read 返回 -1 并且 errno 是 EINTR,则可以打印字符串“Interrupt signal caught\n)”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-21
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多