gpt4 book ai didi

c - 超时调用读取的缺陷

转载 作者:太空宇宙 更新时间:2023-11-03 23:26:15 28 4
gpt4 key购买 nike

我是系统编程的新手,我遇到了这个程序。

#include "apue.h"
static void sig_alrm(int);
int main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
alarm(10);
if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
err_sys("read error");
alarm(0);
write(STDOUT_FILENO, line, n);
exit(0);
}
static void sig_alrm(int signo)
{
/* nothing to do, just return to interrupt the read */
}

该程序旨在为 read() 系统调用提供超时。如果 read() 花费的时间超过 30 秒,则会生成一个信号。现在,

引用教科书(它列出了程序中的2个缺陷,其中一个是)

If system calls are automatically restarted, the read is not interrupted when the SIGALRM signal handler returns. In this case, the timeout does nothing.

我无法理解这句话的意思。谁能解释一下

谢谢。

最佳答案

以下注意事项适用于 Linux <= 2.6.11,但我相信这些概念也适用于最近的内核。

如果一个进程在系统调用时被阻塞,它会被放入等待队列中,处于 TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE 状态。在前一种情况下,内核在收到信号后将其放回 TASK_RUNNING 状态,将进程添加到运行队列并将信号添加到待处理信号列表中。

当进程被调度时,它继续执行系统调用。由于系统调用未完成,可能的返回码是:

  • EINTR
  • ERESTARTNOHAND
  • ERESTART_RESTARTBLOCK
  • ERESTARTSYS
  • ERESTARTNOINTR

在系统调用的退出路径上,检查未决信号。在这种情况下,您的 SIG_ALRM 处理程序被调用,然后内核立即检查系统调用的返回代码。一般来说,可能会出现以下情况:

  • 返回码为EINTR:用户态进程恢复,read的返回值为-EINTR
  • 返回码为ERESTARTNOINTR:系统调用重新执行
  • 返回代码为 ERESTARTSYS:如果设置了 SA_RESTART 标志,则重新执行系统调用,否则 -EINTR 返回给用户空间。
  • 返回代码为 ERESTARTNOHANDERESTART_RESTARTBLOCK:用户模式进程恢复并返回 -EINTR。

重新执行系统调用意味着进程 EIP 减 2,因此被迫再次执行 int 0x80

在这种特定情况下,如果进程在来自 STDIN 的读取系统调用上被阻塞,并且它从 man 7 signal 接收到 SIG_ALRM系统中断部分信号处理程序的调用和库函数:

If a blocked call to one of the following interfaces is
interrupted by a signal handler, then the call will be
automatically restarted after the signal handler returns
if the SA_RESTART flag was used; otherwise the call will
fail with the error EINTR.

* read(2), readv(2), write(2), writev(2), and ioctl(2)
calls on "slow" devices. A "slow" device is one where the
I/O call may block for an indefinite time, for example, a
terminal, pipe, or socket. (A disk is not a slow device
according to this definition.) If an I/O call on a slow
device has already transferred some data by the time
it is interrupted by a signal handler, then the call
will return a success status (normally, the number of bytes
transferred).

这是 ERESTARTSYS 返回代码的行为,即只有在设置了 SA_RESTART 的情况下,系统调用才会在捕获到信号时重新执行,否则它将返回 EINTR(如果尚未传输任何数据)。

所以你书中的第一个陈述

If system calls are automatically restarted [...]

表示

If, upon receiving a signal, SA_RESTART is set

如果是,则系统调用重启,再次阻塞,告警无效。

引用资料:

  • 了解 Linux 内核第 3 版,第 10 章(系统调用),第 11 章(信号)
  • man 7 signal

关于c - 超时调用读取的缺陷,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26990261/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com