【问题标题】:implement ctr-c using sigaction使用 sigaction 实现 ctr-c
【发布时间】:2016-12-21 22:29:33
【问题描述】:

我正在尝试使用 sigaction 来忽略 ctrl-c。代码在最后帮助理解。问题是当我按 ctrl-C 时,出现 ^Cctrl-c 而不是 ^C,并且不打印下一个提示立即。只有在我点击 Enter 之前,它才会开始打印提示。像这样:

> // run the program sigAction
$./sigAction
sigAction>^Cctrl-C
// it is waiting,waiting . Nothing happens until I hit Enter, next prompt comes up.

sigAction>

我正在寻找的结果是当我按 ctrl-C 而不按 Enter 时,应该立即出现下一个提示。问题如下:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <signal.h>
  4 #include <string.h>
  5 // implement ignoring ctrl-C
  6 void sigIntHandler(int signum) {
  7     fprintf(stderr, "ctrl-C\n" );
  8   
  9 }
 10
 11 main()
 12 {
 13
 14     struct sigaction signalAction;
 15     signalAction.sa_handler = sigIntHandler;
 16     sigemptyset(&signalAction.sa_mask);
 17     signalAction.sa_flags = SA_RESTART;
 18     sigaction(SIGINT, &signalAction, NULL);
 19     while(1) {
 20
 21         char block[4096];
 22
 23         printf("sigAction>");
 24         fflush(stdout);
 25
 26         fgets(block, 4096, stdin);
 27        
 28         if (!strcmp(block, "exit\n")) {
 29            exit(1);
 30         }
 31     } 
 32 }       

当我使用 gdb 查看内部时,它会为我提供以下信息:

  (gdb) n
Single stepping until exit from function main,
which has no line number information.
sigAction>^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b15d10 in __read_nocancel () from /lib64/libc.so.6
(gdb) n
Single stepping until exit from function __read_nocancel,
which has no line number information

任何想法都有帮助!提前致谢!

【问题讨论】:

  • 为什么是SA_RESTART?这又回到了中断的系统调用,不是吗?另外,请参阅How to avoid using printf() in signal handlers? 此外,您应该至少按照旧 (C99) 标准和最好是新 (C11) 标准进行编码,这两者都需要显式返回类型 int main(void)
  • 在信号处理程序中可以安全使用的函数数量非常有限。 fprintf 不是其中之一。
  • 非常感谢!

标签: c shell gdb signals


【解决方案1】:

正如我在评论中指出的那样,您并不想使用SA_RESTART。这意味着当read() 收到中断时,读取会在中断后恢复,这会排除您的主程序打印新提示的机会。

这段代码似乎很接近你想要的:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void sigIntHandler(int signum)
{
    fprintf(stderr, "Ctrl-C (%d)\n", signum);
}

int main(void)
{
    struct sigaction signalAction;
    signalAction.sa_handler = sigIntHandler;
    sigemptyset(&signalAction.sa_mask);
    signalAction.sa_flags = 0;      // SA_RESTART;
    sigaction(SIGINT, &signalAction, NULL);

    while (1)
    {
        char block[4096];

        printf("sigAction>");
        fflush(stdout);

        if (fgets(block, 4096, stdin) == 0)
        {
            if (errno == EINTR)
            {
                errno = 0;
                clearerr(stdin);
                fprintf(stderr, "Interrupted!\n");
                continue;
            }
            else
            {
                fprintf(stderr, "EOF spotted\n");
                break;
            }
        }

        if (!strcmp(block, "exit\n"))
        {
            fprintf(stderr, "Exiting\n");
            exit(1);
        }
        printf("Read: [%.*s]\n", (int)(strlen(block) - 1), block);
    }
}

请注意,正式地,您不应该在信号处理程序中使用 printf() 系列函数 — 请参阅 How to avoid using printf() in a signal handler?

示例运行(程序名为sigact):

$ ./sigact
sigAction>Ordinary input
Read: [Ordinary input]
sigAction>Some typing, then an interrupt! ^CCtrl-C (2)
Interrupted!
sigAction>More input?
Read: [More input?]
sigAction>Yes, more input.
Read: [Yes, more input.]
sigAction>And why you type control-D?
Read: [And why you type control-D?]
sigAction>EOF spotted
$ ./sigact
sigAction>Also typing exit works, of course.
Read: [Also typing exit works, of course.]
sigAction>exit
Exiting
$

^C 来自终端驱动程序(在 Mac OS X 10.11.6 上)。 echoctl lflags 第一行末尾的 echoctl 这样做:

$ stty -a
speed 9600 baud; 50 rows; 141 columns;
lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
    -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
    -extproc
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
    -ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
    -dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
    eol2 = <undef>; erase = ^?; intr = ^C; kill = ^X; lnext = ^V;
    min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
    stop = ^S; susp = ^Z; time = 0; werase = ^W;
$

【讨论】:

  • 非常感谢乔纳森!我明白你在说什么。但是我仍然不明白为什么当我按下 ctrl-C 时,它会出现 ^Cctrl-C。我的另一个问题是,如果我有一个解析器函数:yyparse(),我如何将输入传递给解析器?如果输入不是 ctrl-c 或退出,我希望它可以执行像“ls -al”这样的正常输入。非常感谢!
  • ^C 来自终端驱动程序(内核的一部分),至少在我测试的 Mac OS X 上。 Ctrl-C 部分来自中断处理程序中的 fprintf() 调用。当您键入 Control-C 时,终端驱动程序将其解释为“使用终端向进程发送中断”。 (完整的故事比这更复杂,但现在不必关心我们——只要说作业控制、会话、流程组等使事情变得比你需要处理的更复杂。)所以,当你输入 Control-C 时,一个中断被发送到程序,它安排调用处理函数。
  • 知道了!谢谢你的快速反应!你知道评论中关于解析器的另一个问题吗?顺便说一句,我是 shell 编程的新手。有没有调试解析器和语法的好工具?
  • 更新:解决了!
【解决方案2】:

如果你想忽略,那么使用SIG_IGN作为动作,如果不是那么你不是忽略而是捕捉。

当您使用SA_RESTART 时,被中断的系统调用将被重新启动并且不会被中断。 fgets 可能在某些内部系统调用中阻塞,您指定重新启动它。我不记得fgets 在被打断时的行为,但它可能会返回一个NULL 值。您也可以使用read 并控制errnoEINTR

【讨论】:

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