【问题标题】:Strange sigaction() and getline() interaction奇怪的 sigaction() 和 getline() 交互
【发布时间】:2013-10-09 01:42:37
【问题描述】:

我有一个使用 sigaction 设置的信号处理程序,如下所示:

struct sigaction act, oldact;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGALRM);
sigaddset(&act.sa_mask, SIGINT);
sigaddset(&act.sa_mask, SIGTERM);
sigaddset(&act.sa_mask, SIGTSTP);
sigaction(SIGALRM, &act, &oldact);
sigaction(SIGINT, &act, &oldact);
sigaction(SIGTERM, &act, &oldact);
sigaction(SIGTSTP, &act, &oldact);
act.sa_flags = 0;

在此之后,我运行了一个 for 循环来收集输入并将其打印出来(基本上就像 cat 一样)。

char *linebuf = NULL;
size_t n = 0;
int len;
while(1){
    len = getline(&linebuf, &n, stdin);
    if(len>0){
        printf("%s", linebuf);
    }
}

但是,一旦我从处理信号返回,getline() 不再阻塞输入,而是在将errno 设置为EINTR 时始终返回-1,这是一个中断的系统调用。我显然是想打断getline(),但我该如何重置它以便继续阅读输入?

有几个类似的问题并没有真正解决我的问题,但它可能会帮助您更好地理解问题。 12

另一个有趣的消息是,当我使用signal() 进行信号处理时,我没有遇到这个问题,但我改为sigaction(),因为它符合 POSIX。

【问题讨论】:

  • printf(linebuf); 可能不是有效的 C 语法。
  • @hacks,是的,应该是printf("%s", linebuf);
  • 我已将其更改为 printf("%s", linebuf);printf(linebuf); 是有效的 c 语法(确实给出了此警告 warning: format not a string literal and no format arguments [-Wformat-security] 但它不是错误),而且不是问题。
  • @ConnorBlanck,这不是错误,但非常危险。想象一下如果linebuf 包含% 会发生什么。事实上,如果它包含%n,它可能会被用来覆盖你的记忆,这就是为什么长期以来为Windows编写病毒如此容易的原因。

标签: c signals getline signal-handling


【解决方案1】:

您链接到的一个问题实际上有您的答案。但首先,有几个参考。注意getline在内部使用其他函数(可能是read)。

来自read的手册页:

如果 read() 在读取任何数据之前被信号中断,它应该返回 -1 并将 errno 设置为 [EINTR]。

...

... 还有一个附加函数 select(),其目的是暂停直到在指定的文件描述符上检测到指定的活动(要读取的数据、要写入的空间等)。在为那些系统编写的应用程序中,在需要因信号而中断 I/O 的情况(例如键盘输入)中,在 read() 之前使用 select() 是很常见的。

来自select的手册页:

pselect() 函数应检查文件描述符集 ... 以查看它们的某些描述符是否已准备好读取、已准备好写入或有异常情况未决。

因此,您可以使用select 了解何时可以安全地开始阅读。但是,这仍然不能阻止read 以后被打断。 (我只是说这些供您参考)。

要真正清除流的错误标志,如 this answer 到您的链接问题之一状态(尽管在 C++ 中),您需要使用 clearerr 函数清除流上的任何错误标志。请注意,手册页(以及 C 标准)并没有具体说明它还清除“中断”标志,但这只是因为这超出了 C 的范围并且在 POSIX 的范围内。


作为旁注,我想说,请不要使用memset 来清除结构。 C 有一个非常好的方法:

struct sigaction act = {0};

或者如果你想学究气,因为struct sigaction 的第一个元素可能是另一个结构或数组,你可以分配给它的一个强制字段:

struct sigaction act = { .sa_handler = NULL };

【讨论】:

    【解决方案2】:

    一个很晚的答案,但我会分享我最近在处理这个问题时学到的一些东西。当您在 sigaction 结构中将 sa_flags 设置为 0 时,您清除了在收到信号时读取的默认行为。当您使用旧的 signal() 方法时,会保留此默认行为。要在您的情况下恢复这一点,您必须设置 sa_flags = SA_RESTART。但是,在这种情况下,将调用信号处理程序,如果 getline 不在读取过程中,它将继续等待输入。如果信号发生在读取过程中,您仍然会得到 -1,并且您必须在恢复之前 clear() 流。但是,如果读取被阻止等待数据并且信号发生,使用 SA_RESTART,您将在信号处理程序成功执行后立即返回读取。

    我个人希望能够中断 getline(),这样我就可以很好地终止在管道上等待的程序,但遇到了麻烦,因为我使用了旧的 signal() 方法来设置我的处理程序。所以我会得到信号,但 getline 永远不会被中断,因为 read 被阻止等待数据,它只会在我的信号处理程序执行后自动重新启动读取。当我移动到 sigaction 结构并设置 sa_flags=0 时,它中断了 getline 导致它基本上返回 EINTR,我可以向我的进程发送一个信号来告诉它关闭。

    我喜欢这个论坛。我无法计算它帮助解决棘手问题的次数。以为我会回馈。希望这对下一个程序员有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-29
      • 1970-01-01
      • 2015-12-24
      • 1970-01-01
      • 1970-01-01
      • 2015-07-30
      • 2016-12-13
      • 1970-01-01
      相关资源
      最近更新 更多