【问题标题】:Is there any way to get 64-bit time_t in 32-bit programs in Linux?有没有办法在 Linux 的 32 位程序中获得 64 位 time_t?
【发布时间】:2012-12-30 23:07:08
【问题描述】:

在 Windows 上我可以调用:

_time32(__time32_t); // to get 32-bit time_t
_time64(__time64_t); // to get 64-bit time_t

(在 32 位和 64 位程序中)

在 Linux 中有没有办法做到这一点(使用 GCC 编译)?

【问题讨论】:

  • @Benoit 这个问题不是重复的,而是完全不同的。
  • 不是真的...在 32 位 Linux 上,time_t 是一个传统的 32 位有符号数,它的使用寿命只有 25 年。在 64 位 Unix 系统上,它已经是一个 64 位的量。如果你想要一个可移植的类型,你必须定义你自己的适当映射的类型,或者小心地使用time_t。引用的另一个问题有相关信息(但我同意它不是重复的)。

标签: c linux gcc 64-bit time-t


【解决方案1】:

显然,不,这是不可能的。对于初学者,Linux 中只有一个time() 函数,没有time32()time64()

搜索了一会,发现不是libc的错,而罪魁祸首其实是内核。

为了让 libc 获取当前时间,它需要对其执行系统调用:(Source)

time_t time (t) time_t *t;
{
    // ...
    INTERNAL_SYSCALL_DECL (err);
    time_t res = INTERNAL_SYSCALL (time, err, 1, NULL);
    // ...
    return res;
}

系统调用定义为:(Source)

SYSCALL_DEFINE1(time, time_t __user *, tloc)
{
    time_t i = get_seconds();
    // ...
    return i;
}

函数get_seconds() 返回一个unsigned long,如下所示:(Source)

unsigned long get_seconds(void)
{
    struct timekeeper *tk = &timekeeper;

    return tk->xtime_sec;
}

timekeeper.xtime_sec实际上是64位的:(Source)

struct timekeeper {
    // ...
    /* Current CLOCK_REALTIME time in seconds */
    u64                     xtime_sec;
    // ...
}

现在,如果你了解你的 C,你就会知道 unsigned long 的大小实际上是依赖于实现的。在我这里的 64 位机器上,它是 64 位的;但在我这里的 32 位机器上,它是 32 位的。 可能在某些 32 位实现上可能是 64 位,但不能保证。

另一方面,u64 始终是 64 位的,因此在最基础上,内核以 64 位类型跟踪时间。为什么它会继续以unsigned long 的形式返回它,它不能保证是 64 位长,这超出了我的理解。

最后,即使 libc 会强制 time_t 保存一个 64 位值,它也不会改变任何事情。

您可以将您的应用程序深深地绑定到内核中,但我认为这甚至不值得。

【讨论】:

  • 所以,写一个补丁并发布它。对于 libc (typedef long long int time_t) 和内核 long long int get_seconds(void)
  • @user877329 更改现有函数的类型会破坏很多事情,因此必须为 64 位时间引入新的系统调用。见下文
【解决方案2】:

上面的许多答案都说这是不可能的,但那是完全不正确当时是不可能的,但人们已经talking about fixing it 多年了。最后,32 位平台上的 64 位时间支持是 introduced to the Linux 5.1 kernel,并添加了新的 *time64 syscalls。查看this table,您可以看到这些系统调用仅在 32 位平台上可用。现在,如果您正在为 32 位系统编写代码,您可以直接调用 clock_gettime64(从内联汇编或带有 syscall() 的 C)来获取当前时间

不过,在那之后你就完全靠自己了。如果您想要完整的用户空间支持,您必须使用 Linux 5.6 或更高版本以及 musl 1.2+ 或 glibc 2.32+。只需重建您的代码,您的 time_t 将变为 64 位长

  • 所有用户空间都必须使用 64 位 time_t 编译,即将推出的 musl-1.2 和 glibc-2.32 版本以及已安装的 linux-5.6 或更高版本的内核头文件都将支持它。

  • 直接使用系统调用接口的应用程序需要移植到使用 linux-5.1 中添加的time64 系统调用来代替现有的系统调用。这会影响futex()seccomp() 的大多数用户以及拥有自己的运行时环境而不是基于libc 的编程语言。

https://lkml.org/lkml/2020/1/29/355?anz=web

更多信息

【讨论】:

  • 在我的 32 位 Linux 5.4 上,我无法调用 clock_gettime64 等。它告诉我未定义的引用。我需要包含/定义/安装什么才能使其正常工作?
  • @arminb 你怎么称呼它?这是一个系统调用,而不是一个 C 函数。在 C 中,您需要使用 syscall(),如上所述,但您需要知道如何放置系统调用号以及如何放置所有寄存器值
  • @arminb 你可以在musl 中看到它是如何被调用的。另见this。如果这没有帮助,请在另一个问题中提问
  • 刚刚弹出来补充:在musl上,我相信glibc,没有必要为了使用64位time_t而拥有一个支持time64的内核。在较旧的内核上,您将被限制不能设置超过 Y2038 的当前时间或文件时间戳,但您仍然可以将新的 ABI 用于应用程序,并且当您迁移到较新的内核时,它们将准备就绪。
【解决方案3】:

没有time64()/time32() 函数包含在标准库中。

标准头文件中不考虑time32_t/time64_t 定义。

time_t 被定义为time.htypedef __time_t time_t

经过一长串的重新定义,您会发现__time_t 在 32 位机器上定义为 32 位,在 64 位机器上定义为 64 位。

【讨论】:

    【解决方案4】:

    使用这个库:https://github.com/evalEmpire/y2038

    这个项目的目标是提供一个 POSIX 的替代品 time.h 可以在具有 32 位 time_t 的机器上工作,但不能 遭受 2038 错误。这将允许 C 程序员 2038 安全,无需将其软件重写为新界面。 它在仍然使用系统时区数据库的同时执行此操作。

    【讨论】:

      【解决方案5】:

      如果你真的需要这个,为什么不自己动手呢?

      typedef int32_t my_time32;
      typedef int64_t my_time64;
      
      
      my_time32 get_mytime32() {
          if (sizeof(time_t) == sizeof(my_time32))
              return time(NULL);
          else {
              /* Check for overflow etc. here... */
              return (my_time32)(time(NULL));
          }
      }
      

      get_mytime64() 也是如此。

      如果您不关心溢出问题,由于 C 的隐式数字转换,一个简单的 return time(NULL); 将适用于这两个函数。

      【讨论】:

      • 我不认为一个简单的返回时间(NULL);适用于 32 位平台上的 get_mytime64() - 它只会截断。
      • @rsaxvc 是的,因此有“在此处检查溢出等”评论。一般来说,您在处理 32 位时间时遇到问题
      猜你喜欢
      • 2011-12-20
      • 2011-05-16
      • 2012-06-28
      • 2012-09-17
      • 2015-05-19
      • 1970-01-01
      • 2010-10-18
      • 2020-05-17
      • 2014-12-24
      相关资源
      最近更新 更多