[Bug] [libc] timer_gettime 拆秒前将总纳秒截断为 uint32_t,剩余时间查询错误

Đang mở Phù hợp với người mới
#11,924 7 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

@Yunlk đang làm issue này rồi.

Từ ngày 24/10/2026.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
68/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c
Lĩnh vực
backend, operating-systems

Hướng nghiên cứu

Đọc components/libc/compilers/common/ctime.c, đặc biệt là timer_gettime() quanh các dòng được báo cáo 1337–1375, và theo dõi cách số tick còn lại của bộ đếm được chuyển đổi sang nanô giây. Ví dụ số học độc lập cho thấy việc cắt cụt; hãy tái hiện ví dụ đó, sau đó xác minh kết quả với thời gian còn lại là 10 giây sau khi thay đổi. Giữ lại các kiểm tra tràn vòng của bộ đếm và phạm vi phép nhân trung gian, đồng thời xác nhận cấu hình bộ định thời POSIX bật đoạn mã này.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

in progress
RT-Thread Version

master, commit a59e72fb5c2f272761a2813fc9bd9fe18eb29d7d.

Affected area

libc / POSIX timers, components/libc/compilers/common/ctime.c, timer_gettime().

Hardware / Architecture / Toolchain

采用源码级算术隔离复现,不依赖特定开发板。该提交中的受影响实现由 RT_USING_POSIX_TIMER && RT_USING_CLOCK_TIME 启用。本报告尚未进行目标板复现。

问题描述 / Describe the bug

timer_gettime() 将总剩余纳秒存入 rt_uint32_t,之后才拆分秒和纳秒:

rt_uint32_t seconds, nanoseconds;
/* ... */
nanoseconds = ((remain_cnt - rt_clock_time_get_counter())
               * rt_clock_time_get_res_scaled()) / RT_CLOCK_TIME_RESMUL;
seconds     = nanoseconds / NANOSECOND_PER_SECOND;
nanoseconds = nanoseconds % NANOSECOND_PER_SECOND;

固定版本源码 L1337–1375

即使计数器表达式正确算出 10 秒,即 10,000,000,000 ns,赋给 32 位无符号变量时也会变成 1,410,065,408 ns。查询结果因此是 {tv_sec=1, tv_nsec=410065408},而不是 {10, 0}。

可运行的隔离复现 / Standalone arithmetic reproduction

下面明确模拟计数器换算已经正确得到总纳秒的情况,不调用宿主 libc 的 timer_gettime():

#include <assert.h>
#include <stdint.h>
#include <stdio.h>

int main(void)
{
    uint64_t converted_ns = UINT64_C(10000000000);
    uint32_t seconds, nanoseconds;

    nanoseconds = converted_ns;
    seconds = nanoseconds / 1000000000;
    nanoseconds %= 1000000000;

    printf("%u.%09u\n", (unsigned)seconds, (unsigned)nanoseconds);
    assert(seconds == 1 && nanoseconds == 410065408);
    return 0;
}

输出:1.410065408;预期剩余时间为 10.000000000。

此前还在本地旧版同一函数的隔离计数器桩中复现过该结果;最新上游同样保留上述窄化顺序。完整目标端验证仍待进行。

Scope / Suggested direction
  • 问题从总纳秒超过 UINT32_MAX(约 4.295 秒)起出现,不是几十年后的时间范围问题。
  • 这是剩余时间查询结果错误,不表示定时器一定提前或延后触发。
  • 后面的 (rt_int32_t)seconds 不是这里的根因,数据在拆秒之前已经丢失。
  • 建议总纳秒使用足够宽的类型,再拆分秒和纳秒;或在安全的单位下先拆秒、再换算余数。还应保留对计数器回绕及中间乘法范围的检查。
引入提交和版本 / Regression history
  • 引入提交:0eb75ced70,2023-07-17,[time]时钟框架重构 (#7794);首个包含该提交的正式 release 为 v5.0.2。
  • 此前使用 64 位 remaining 保存总纳秒,再分别赋值给秒和纳秒字段;该提交改为将总纳秒直接赋给 32 位 nanoseconds,然后再拆秒。
  • rt_uint32_t seconds, nanoseconds 的声明本身在更早实现中已经存在,但当时用于保存拆分后的字段,并非这次回归的来源。
  • 后续 743b614875 的时间子系统重构更换了计数接口名称,但保留了该窄化顺序。
其他实现参考 / Comparison

Linux 使用 64 位 ktime_t 转 timespec64;Zephyr 则先从剩余毫秒中拆秒,再将余数转换为纳秒。两种方式均避免这里的总纳秒 32 位中转。这不是对这些 OS 所有计时边界行为的保证。

Ngôn ngữ chính
C
Star
12.3k
Fork
5.5k
Merge trung bình
7 ngày 8 giờ
Pull request đã merge (30 ngày)
65

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của RT-Thread/rt-thread

Tất cả issue của RT-Thread/rt-thread

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.