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

Aperta Adatta ai principianti
#11,924 7 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@Yunlk ci sta già lavorando.

Dal 24/10/2026.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
68/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c

Direzione di ricerca

Leggi components/libc/compilers/common/ctime.c, in particolare timer_gettime() intorno alle righe segnalate 1337–1375, e segui la conversione dei tick rimanenti del contatore in nanosecondi. L’esempio aritmetico autonomo dimostra il troncamento; riproducilo, quindi verifica il risultato per un tempo rimanente di 10 secondi dopo la modifica. Mantieni i controlli per il wraparound del contatore e per l’intervallo della moltiplicazione intermedia, e conferma che la configurazione del timer POSIX abilita questo codice.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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 所有计时边界行为的保证。

Lingua principale
C
Stelle
12.3k
Fork
5.5k
Merge medio
4g 9h
PR unite (30g)
37

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di RT-Thread/rt-thread

Tutte le issue di RT-Thread/rt-thread

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.