【问题标题】:clock_gettime fails in chrooted Debian etch with CLOCK_PROCESS_CPUTIME_IDclock_gettime 在使用 CLOCK_PROCESS_CPUTIME_ID 的 chrooted Debian 蚀刻中失败
【发布时间】:2012-08-21 23:17:49
【问题描述】:

我已经在 Ubuntu 12.04(64 位)下设置了一个 chrooted Debian Etch(32 位),并且似乎 clock_gettime() 与 CLOCK_MONOTONIC 一起工作,但在 CLOCK_PROCESS_CPUTIME_ID 和 CLOCK_THREAD_CPUTIME_ID 上都失败了。 errno 设置为 EINVAL,根据手册页,这意味着“此系统不支持指定的 clk_id。”

所有三个时钟在 chrooted Debian 之外和 64 位 chrooted Debian etch 中都可以正常工作。

有人可以向我解释为什么会出现这种情况以及如何解决它吗?

非常感谢。

【问题讨论】:

  • 我找不到任何合理的解释。您可以在 strace 下运行测试并显示它为 clock_gettime 调用打印的内容吗?您的内核版本也很高兴知道。
  • 我在我能想到的最简单的例子上运行了 strace,这是输出 gist.github.com/3436182。代码位于gist.github.com/3436249。我是 stackoverflow 的新手,如果我应该编辑原始问题以包含这些信息,请告诉我。
  • 我在主机系统和chroot系统下运行'uname -r'得到的内核版本是3.2.0-29-generic。
  • clock_gettime 根本没有出现在 strace 中。这意味着 EINVAL 来自 glibc 或 vdso。这是一个严峻的调试挑战。

标签: c++ ubuntu debian clock chroot


【解决方案1】:

我还不知道原因,但我有一些不适合评论框的想法。

首先,您可以通过将测试程序编译为 C 而不是 C++ 并且不将其链接到 libpthread 来简化测试程序。 -lrt 应该足以获得 clock_gettime。此外,使用-static 编译它可以使跟踪更容易,因为动态链接器启动的东西不会在那里。

静态链接甚至可能改变clock_gettime 的行为。值得一试,看看它是否可以解决这个错误。

我想看到的另一件事是这个绕过 vdso 的测试程序的输出:

#define _GNU_SOURCE
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/syscall.h>

int main(void)
{
    struct timespec ts;
    if(syscall(SYS_clock_gettime, CLOCK_PROCESS_CPUTIME_ID, &ts)) {
        perror("clock_gettime");
        return 1;
    }
    printf("CLOCK_PROCESS_CPUTIME_ID: %lu.%09ld\n",
           (unsigned long)ts.tv_sec, ts.tv_nsec);
    return 0;
}

有和没有-static,如果失败,添加strace

更新(其实跳过这个。去第二次更新)

几个更简单的测试想法:

  1. 在 Ubuntu 主机系统中编译并运行一个 32 位测试程序,方法是在 gcc 命令中添加-m32。可能是内核的 32 位兼容模式导致了该错误。如果是这种情况,那么无论链接到哪个 libc,32 位版本都会失败。
  2. 将你在 Debian 下编译的非静态测试程序复制到 Ubuntu 主机系统并尝试在那里运行。行为变化将指向 libc。

然后是艰难的事情的时候了。查看反汇编代码,并可能在 gdb 中单步执行。我不想让你自己做,而是想要一份你正在运行的代码的副本。在我能得到的地方上传一个静态编译的失败测试程序。您的内核提供的 32 位 vdso 的副本也可能很有趣。要提取 vdso,请运行以下程序(在 32 位 chroot 中编译),该程序将创建一个名为 vdso.dump 的文件,并将其上传。

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

static int getvseg(const char *which, const char *outfn)
{
  FILE *maps, *outfile;
  char buf[1024];
  void *start, *end;
  size_t sz;
  void *copy;
  int ret;
  char search[strlen(which)+4];

  maps = fopen("/proc/self/maps", "r");
  if(!maps) {
    perror("/proc/self/maps");
    return 1;
  }
  outfile = fopen(outfn, "w");
  if(!outfile) {
    perror(outfn);
    fclose(maps);
    return 1;
  }

  sprintf(search, "[%s]\n", which);
  while(fgets(buf, sizeof buf, maps)) {
    if(strlen(buf)<strlen(search) ||
       strcmp(buf+strlen(buf)-strlen(search),search))
      continue;
    if(sscanf(buf, "%p-%p", &start, &end)!=2) {
      fprintf(stderr, "weird line in /proc/self/maps: %s", buf);
      continue;
    }
    sz = (char *)end - (char *)start;
    /* copy because I got an EFAULT trying to write directly from vsyscall */
    copy = malloc(sz);
    if(!copy) {
      perror("malloc");
      goto fail;
    }
    memcpy(copy, start, sz);
    if(fwrite(copy, 1, sz, outfile)!=sz) {
      if(ferror(outfile))
        perror(outfn);
      else
        fprintf(stderr, "%s: short write", outfn);
      free(copy);
      goto fail;
    }
    free(copy);
    goto success;
  }
  fprintf(stderr, "%s not found\n", which);

fail:
  ret = 1;
  goto out;
success:
  ret = 0;
out:
  fclose(maps);
  fclose(outfile);
  return ret;
}

int main(void)
{
  int ret = 1;
  if(!getvseg("vdso", "vdso.dump")) {
    printf("vdso dumped to vdso.dump\n");
    ret = 0;
  }
  if(!getvseg("vsyscall", "vsyscall.dump")) {
    printf("vsyscall dumped to vsyscall.dump\n");
    ret = 0;
  }
  return ret;
}

更新 2

我通过下载一个 etch libc 复制了这个。这绝对是 glibc 愚蠢造成的。它不是用于clock_gettime 的简单系统调用包装器,而是有一大堆预处理器意大利面条,最终导致“你不能使用我们没有预先批准的clockid”。你不会让它与那个旧的 glibc 一起工作。这让我们想到了一个我不想问的问题:您为什么还要尝试使用过时的 Debian 版本?

【讨论】:

  • 在 chroot 中将原始测试程序编译为 C 并使用 -static 会给出以下非常短的跟踪,gist.github.com/3451000,正如您所指出的,它根本不包含 clock_gettime,这与 strace 的输出不同主机Ubuntu系统下的同一个程序,gist.github.com/3451017
  • 您提供的测试程序似乎有效。如果没有 -static,我会得到以下范围内的东西:CLOCK_PROCESS_CPUTIME_ID: 0.000882401。使用-static,输​​出在CLOCK_PROCESS_CPUTIME_ID: 0.000348041的范围内,对应的strace输出还包含对clock_gettime的调用,gist.github.com/3451107
  • 基本上,我正在尝试构建一些共享库,以便它们可以在具有旧库(GLIBC 2.3.6 和可能的其他库)的服务器上运行。经过多次谷歌搜索,似乎 chrooted 构建环境是最佳选择,而 Debian etch 具有 GLIBC 2.3.6。我对这些事情没有太多经验,所以我可能错过了一些明显和简单的选择。
  • 我还希望同一个共享库在旧系统和新系统上都可以工作,并且将动态链接的测试程序移出 chroot 确实有效,所以至少这是一个优点。顺便说一句,非常感谢您的帮助。
  • 如果你在你的库中包含你自己的 clock_gettime 函数,就像我向你展示的那样使用syscall() 实现,那么至少应该处理来自库内的任何调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
  • 1970-01-01
  • 2015-07-15
  • 2019-11-12
  • 1970-01-01
相关资源
最近更新 更多