【问题标题】:read() system call doesn't fail when an alarm signal is received收到警报信号时,read() 系统调用不会失败
【发布时间】:2013-09-04 02:58:58
【问题描述】:

这是(部分)我的服务器端代码

void timeout_handler(int value) {
    printf("Handler\n");
    return;
}

int main (int argc, char **argv) {
    [...]
    signal(SIGALRM, timeout_handler);
    alarm(seconds);
    int result = read(input_socket, buffer, sizeof(buffer));
    if (result == -1 && errno == EINTR) {
        printf("read() failed\n");
    }
    [...]
}

input_socket 是一个与客户端正确连接的 TCP 套接字(如果我从客户端发送数据,服务器会接收它们)。

作为警报信号的测试,我尝试只打开并连接套接字客户端而不发送任何数据。 我希望输出像

Handler
read() failed

但结果只是Handler消息,然后进程仍然处于活动状态。

为什么read() 不会因errno=EINTR 而失败?

【问题讨论】:

  • 注意:您不应该在信号处理程序中调用 printf()(和朋友);它们不是异步安全的。
  • 为什么read() 会失败?它正在等待某事出现。 If a process attempts to read from an empty pipe, then read(2) will block until data is available.
  • 您在哪个操作系统上观察到这种行为? BSD?
  • printf 的使用仅用于测试目的。而且我认为read() 会失败,因为该进程接收到一个中断信号(alarm(seconds)),并且这些信号会中断慢速系统调用。我正在 OS X 上开发。

标签: c macos signals alarm system-calls


【解决方案1】:

在 OSX 上,某些系统调用在被信号中断时默认重新启动

siginterrupt() 可用于更改此行为。以下行(在调用 alarm() 之前的某处)应该可以让程序按照 OP 的预期运行:

siginterrupt(SIGALRM, 1);

来自 OSX 文档(我强调):

对于某些系统调用,如果在调用执行过程中捕获到信号并且过早调用 终止,呼叫将自动重新开始。 使用 signal(3) 安装的任何处理程序都将具有 SA_RESTART 标志设置,意味着任何可重新启动的系统调用都不会在收到信号时返回。 受影响的系统调用包括 read(2)、write(2)、sendto(2)、recvfrom(2)、sendmsg(2) 和 recvmsg(2) 在通信通道或低速设备上以及在 ioctl(2) 或 wait(2) 期间。然而,调用 已经提交的不会重新启动,而是返回部分成功(例如, 短读计数)。可以使用 siginterrupt(3) 更改这些语义。

这在 Linux 上是不同的,例如,重新启动可能被中断的系统调用需要由发送的信号明确请求。

【讨论】:

  • 顺便说一句:linux 主要模拟 BSD 行为,具体取决于 libc 版本和 _BSD_SOURCE 和/或 _GNU_SOURCE 的定义。 (无论如何最好使用 sigaction ...)
  • 谢谢!这行得通!但同时使用sigaction()(而不是signal())和sa_flags = 0 也能解决问题!
  • @JustTrying:我猜到了,但不确定也无法测试。
  • @wildplasser: +1 sigaction() :-)
猜你喜欢
  • 2015-12-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多