1

我试图了解何时clock_gettime()会导致错误。手册页列出了以下两种可能性:

  1. EFAULT tp 指向可访问地址空间之外。
  2. EINVAL 此系统不支持指定的 clk_id。

很容易触发EINVAL错误,但我无法clock_gettime()设置errnoEFAULT. 相反,内核发送一个 SIGSEGV 信号来终止程序。例如,在以下代码中:

#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
    struct timespec tp;
    double time;

    if (clock_gettime(CLOCK_MONOTONIC, &tp + 4096) == -1) {
        if (errno == EINVAL) {
            perror("EINVAL");
            return EXIT_FAILURE;
        } else if (errno == EFAULT) {
            perror("EFAULT");
            return EXIT_FAILURE;
        } else {
            perror("something else");
            return EXIT_FAILURE;
        }
    }

    time = tp.tv_sec + 1e-9 * tp.tv_nsec;
    printf("%f\n", time);
}

Linux 内核如何在触发分段错误和让系统调用返回之间做出选择-EINVAL?什么时候会选择做后者?如果内核总是发送信号,是否真的需要检查是否errno等于EFAULT

我正在运行 Linux 内核 4.15,并使用(使用 clang v6.0)编译了该程序: clang -g -O0 -Wall -Wextra -Wshadow -Wstrict-aliasing -ansi -pedantic -Werror -std=gnu11 file.c -o file

4

2 回答 2

2

clock_gettime可能不是作为系统调用执行,而是在用户空间中作为vdso的一部分执行。syscall如果您实际上通过使用with作为参数的函数来执行系统调用SYS_clock_gettime,我希望您会看到EFAULT.

话虽如此,EFAULT您不应该期望能够依赖它。一旦您将无效指针传递给需要有效指针作为其接口契约一部分的函数,您就会有未定义的行为,并且段错误或错误只是众多可能的表现形式之一。从这个角度来看,这是一个EFAULT甚至被记录在案的错误。

于 2018-11-19T23:25:46.203 回答
0

我试图了解 clock_gettime() 何时会导致错误。

好的。

Linux 内核如何在触发分段错误和让系统调用返回 -EINVAL 之间做出选择?什么时候会选择做后者?

这很简单。有一些检查以防它们为真函数设置 errno。如果您访问受保护的内存区域,内核会将 SIGSEGV 发送到您的进程。

如果您从 glibc 函数中检查 __clock_gettime,您会看到:

switch (clock_id)
    {
#ifdef SYSDEP_GETTIME
      SYSDEP_GETTIME;
#endif

#ifndef HANDLED_REALTIME
    case CLOCK_REALTIME:
      ...
      break;
#endif

    default:
#if HP_TIMING_AVAIL
      if ((clock_id ...) == CLOCK_THREAD_CPUTIME_ID)
           ...
      else
#endif
            __set_errno (EINVAL);
      break;

glibc 包装器集的 EINVAL 以防出现一些奇怪的 clock_id 值。

在未定义的行为中取消引用任何有效内存区域之外的指针值并产生鼻恶魔。在 Linux 上,SIGSEGV是发送给试图写入受保护内存区域的进程的信号。

以下代码会产生恶魔并应引发 SIGSEGV:

struct timespec tp;
*(&tp + 4096) = (struct timespec){0};

以下代码也是如此:

struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp + 4096)

如果内核总是发送信号,

并不真地。如果碰巧sizeof(struct timespec)从 开始的字节&tp + 4096不在受保护的内存区域内,内核将不会发送任何信号,因为它会认为,您在自己的内存中写入。

是否真的有必要检查errno是否等于EFAULT?

没有必要检查任何错误。我认为您将解释错误与检查错误混合在一起。如果您的机器遵循您提到的规范,如果clock_gettime返回 EFAULT 您可以编写程序,因此它假定您机器上的底层实现clock_gettime遵循.linux 手册clock_gettime页。但是,正如您所发现的那样,它并没有发生,而是发生了未定义的行为并且内核引发了 SIGSEGV。这仅意味着该clock_gettime功能的底层实现不遵循手册。POSIX _没有指定 EFAULT errno 代码。但是我相信可能存在可能返回 EFAULT errno 或任何其他 errno 代码的实现。但是,当收到 EFAULT 错误时,您希望您的程序做什么?如何从此类错误中恢复?如果这些问题对您有任何意义,那么为该clock_gettime函数编写一个 EFAULT 处理程序可能是合理的。

请注意,您使用的是 Linux。Linux、kernel 和 glibc 大多是根据 GNU General License 或 GNU Lesser General License 获得许可的,其中包含以下内容:

由于图书馆是免费许可的,因此在适用法律允许的范围内,图书馆不提供任何保证。除非另有说明,否则版权所有者和/或其他方按“原样”提供图书馆,不提供任何形式的明示或默示保证,包括但不限于对适销性和特定用途适用性的默示保证. 图书馆的质量和性能的全部风险由您承担。如果图书馆被证明有缺陷,您将承担所有必要的服务、修理或更正的费用。

这个问题值得信赖:您相信您的系统clock_gettime()遵循 Linux 手动实现吗?我不。如果您的系统是 POSIX 证书,您可以更加信任它们将按照手册所述运行的功能。没有人向您保证,它起作用只是许多勤奋的人的善意。

于 2018-11-19T23:56:23.507 回答