You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM架构Linux下GNU C如何实现2us级硬件寄存器读写短延迟

ARM架构Linux下微秒级短延迟解决方案

问题根因

你当前使用的nanosleep、usleep都属于睡眠类延时函数,调用后内核会将当前进程挂起让出CPU,等待超时后再重新调度执行。Linux系统默认的进程调度粒度通常在100us~1ms区间,因此即使你设置1ns的延时,实际也会等待至少一个调度时间片,这是出现100us额外延时的根本原因,这类函数本身就不适合微秒级的短延时场景。

可行方案

方案1:用户态高精度时间戳忙等待(通用性最强,无需内核态权限)

直接通过高精度时钟接口获取时间戳循环判断,直到达到目标延时,完全规避调度开销,2us级的延时精度完全可以满足要求。
示例代码:

#include <time.h>

void busy_wait_us(unsigned long us) {
    struct timespec start, now;
    // 使用单调时钟,不受系统时间修改、NTP调整影响
    clock_gettime(CLOCK_MONOTONIC, &start);
    unsigned long target_nsec = us * 1000UL;
    while (1) {
        clock_gettime(CLOCK_MONOTONIC, &now);
        unsigned long delta_nsec = (now.tv_sec - start.tv_sec) * 1000000000UL + (now.tv_nsec - start.tv_nsec);
        if (delta_nsec >= target_nsec) {
            break;
        }
    }
}

使用注意:

  • 如内核支持,可将CLOCK_MONOTONIC替换为CLOCK_MONOTONIC_RAW,完全避开NTP时间调整,精度更高。
  • 忙等待会占用当前CPU核心,若需更高稳定性可将进程绑定到固定CPU核心,避免进程迁移带来的误差:可通过sched_setaffinity系统调用实现。
  • 如果是ARMv8架构,还可以直接读取架构计时器寄存器cntvct_el0做循环判断,比clock_gettime开销更小、精度更高,仅需提前读取cntfrq_el0寄存器获取计时器频率即可,示例如下:
// 读取ARMv8架构计时器计数
static inline unsigned long long get_arch_timer_cnt() {
    unsigned long long val;
    asm volatile("mrs %0, cntvct_el0" : "=r" (val));
    return val;
}

// 读取ARMv8架构计时器频率,单位Hz
static inline unsigned long get_arch_timer_freq() {
    unsigned long val;
    asm volatile("mrs %0, cntfrq_el0" : "=r" (val));
    return val;
}

void busy_wait_us_armv8(unsigned long us) {
    static unsigned long timer_freq = 0;
    if (timer_freq == 0) {
        timer_freq = get_arch_timer_freq();
    }
    unsigned long long start = get_arch_timer_cnt();
    unsigned long long wait_cnt = (unsigned long long)us * timer_freq / 1000000UL;
    while (get_arch_timer_cnt() - start < wait_cnt);
}

方案2:内核态驱动场景直接使用内核内置延时函数

如果你是在内核态开发驱动操作硬件寄存器,直接调用内核提供的udelay(2)即可,该函数是内核基于平台定时器实现的忙等延时,原生支持微秒级精度,完全满足你的需求。
注意:udelay的入参建议不要超过1000,更长的延时请使用mdelay或睡眠类延时函数。

方案3:提升调度优先级降低干扰

如果需要进一步降低延时被中断拉长的概率,可以将进程设置为实时调度优先级,仅需root权限即可:

#include <sched.h>

struct sched_param sp = {.sched_priority = 99 };
// 设置为SCHED_FIFO实时调度策略,优先级最高
sched_setscheduler(0, SCHED_FIFO, &sp);

设置后进程调度优先级远高于普通进程,忙等过程被其他进程抢占的概率大幅降低,延时稳定性更好。

内容的提问来源于stack exchange,提问作者Martin Vana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 11:09:04