当前位置: 首页 > news >正文

Linux中计时相关函数的实现

一、时间比较宏time_after

#define time_after(a,b)         \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)(b) - (long)(a) < 0))
#define time_before(a,b)        time_after(b,a)
#define time_after_eq(a,b)      \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b)     time_after_eq(b,a)

1. 宏定义分析

1.1. time_after(a,b)

第一行:

(typecheck(unsigned long, a) &&
  • typecheck(unsigned long, a) 是类型检查宏
  • 确保参数 aunsigned long 类型
  • 如果不是该类型,编译时会报错
  • && 表示逻辑与,所有条件都必须满足

第二行:

typecheck(unsigned long, b) &&
  • 同样对参数 b 进行类型检查
  • 确保 b 也是 unsigned long 类型

第三行:

((long)(b) - (long)(a) < 0)

这是核心的逻辑部分:

  • 将两个 unsigned long 参数强制转换为 long 类型,进行有符号运算
  • 计算 (long)(b) - (long)(a)
  • 检查结果是否小于 0

1.2. time_before(a,b)

#define time_before(a,b) time_after(b,a)
  • 简单地调换参数顺序调用 time_after

1.3. time_after_eq

  • 和上述类似

二、32位系统获取64位jiffies函数get_jiffies_64

u64 get_jiffies_64(void)
{unsigned long seq;u64 ret;do {seq = read_seqbegin(&xtime_lock);ret = jiffies_64;} while (read_seqretry(&xtime_lock, seq));return ret;
}

函数整体理解

u64 get_jiffies_64(void)
  • 返回:u64(64位无符号整数)
  • 作用:安全地读取64位jiffies计数器,避免在32位系统上读取时发生数据不一致

变量声明

    unsigned long seq;u64 ret;
  • seq:顺序锁的序列号,用于检测读期间是否有写操作发生
  • ret:保存读取到的jiffies_64值

核心循环机制

    do {seq = read_seqbegin(&xtime_lock);ret = jiffies_64;} while (read_seqretry(&xtime_lock, seq));

这是一个读-验证-重试的循环,确保读取到一致的数据

步骤1:开始读取序列

seq = read_seqbegin(&xtime_lock);
  • read_seqbegin():读取顺序锁的当前序列号
  • 如果序列号是偶数,表示没有写操作在进行,可以安全读取
  • 如果序列号是奇数,表示有写操作正在进行,会自旋等待直到写操作完成

步骤2:读取数据

ret = jiffies_64;
  • 读取64位jiffies值到局部变量ret
  • 在32位系统上,这需要两次32位内存读取(高32位和低32位)

步骤3:验证数据一致性

while (read_seqretry(&xtime_lock, seq));
  • read_seqretry():检查序列号是否发生了变化
  • 如果序列号与开始时相同,说明读取期间没有写操作,数据一致
  • 如果序列号变化了,说明在我们读取过程中有写操作发生,需要重新读取

三、jiffies时间和其他表示法转换timespec_to_jiffies

/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */
#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
#define NSEC_PER_SEC (1000000000L)
# define MAX_SEC_IN_JIFFIES \(long)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC) / NSEC_PER_SEC)
#define SEC_CONVERSION ((unsigned long)((((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) +\TICK_NSEC -1) / (u64)TICK_NSEC))
#if HZ >= 12 && HZ < 24
# define SHIFT_HZ       4
#elif HZ >= 24 && HZ < 48
# define SHIFT_HZ       5
#elif HZ >= 48 && HZ < 96
# define SHIFT_HZ       6
#elif HZ >= 96 && HZ < 192
# define SHIFT_HZ       7
#elif HZ >= 192 && HZ < 384
# define SHIFT_HZ       8
#elif HZ >= 384 && HZ < 768
# define SHIFT_HZ       9
#elif HZ >= 768 && HZ < 1536
# define SHIFT_HZ       10
#else
# error You lose.
#endif
#define SEC_JIFFIE_SC (31 - SHIFT_HZ)
#define SH_DIV(NOM,DEN,LSH) (   ((NOM / DEN) << LSH)                    \+ (((NOM % DEN) << LSH) + DEN / 2) / DEN)
#define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)  /* For divider */
/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8))
static __inline__ unsigned long
timespec_to_jiffies(const struct timespec *value)
{unsigned long sec = value->tv_sec;long nsec = value->tv_nsec + TICK_NSEC - 1;if (sec >= MAX_SEC_IN_JIFFIES){sec = MAX_SEC_IN_JIFFIES;nsec = 0;}return (((u64)sec * SEC_CONVERSION) +(((u64)nsec * NSEC_CONVERSION) >>(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;}

1. 基础常量定义

#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
  • 计算每个tick的纳秒数
  • 1000000UL * 1000 = 1,000,000,000(10亿,即1秒的纳秒数)
  • ACTHZ 是实际的系统时钟频率
  • 公式:TICK_NSEC = NSEC_PER_SEC / ACTHZ
  • 使用 SH_DIV 进行高精度除法
#define NSEC_PER_SEC (1000000000L)
  • 1秒 = 10亿纳秒

2. 最大限制定义

#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
  • ~0UL 是所有位为1的无符号长整数
  • >> 1 右移一位得到最大有符号正数
  • -1 再减1,为计算留出安全余量
  • 例如32位:0xFFFFFFFF → 0x7FFFFFFF → 0x7FFFFFFE
#define MAX_SEC_IN_JIFFIES \(long)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC) / NSEC_PER_SEC)
  • 计算jiffies能表示的最大秒数
  • 公式:MAX_SEC = (MAX_JIFFY_OFFSET × TICK_NSEC) / NSEC_PER_SEC
  • 使用u64避免乘法溢出

3. 系统时钟频率相关

#if HZ >= 12 && HZ < 24
# define SHIFT_HZ       4
// ... 其他范围
#endif
  • 根据HZ值选择最优的移位值
  • 用于后续的缩放计算优化
#define SEC_JIFFIE_SC (31 - SHIFT_HZ)
  • 秒到jiffies的缩放因子

4. 转换常数定义

#define SEC_CONVERSION ((unsigned long)((((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) +\TICK_NSEC -1) / (u64)TICK_NSEC))

这是最关键的预计算常数:

  1. (u64)NSEC_PER_SEC << SEC_JIFFIE_SC:将1秒的纳秒数左移缩放
  2. + TICK_NSEC - 1:向上取整,确保不会因为截断而少算
  3. / (u64)TICK_NSEC:除以每个tick的纳秒数
  4. 结果:缩放后的"每秒对应的jiffies数"

实际意义:

SEC_CONVERSION = ceil( (NSEC_PER_SEC << SEC_JIFFIE_SC) / TICK_NSEC )

5. timespec_to_jiffies 函数详解

static __inline__ unsigned long
timespec_to_jiffies(const struct timespec *value)
{unsigned long sec = value->tv_sec;long nsec = value->tv_nsec + TICK_NSEC - 1;
  • 提取秒和纳秒部分
  • + TICK_NSEC - 1:纳秒部分向上取整,确保足够的纳秒数会计入一个jiffy
    if (sec >= MAX_SEC_IN_JIFFIES){sec = MAX_SEC_IN_JIFFIES;nsec = 0;}
  • 溢出保护:如果超过最大可表示时间,截断到最大值
    return (((u64)sec * SEC_CONVERSION) +(((u64)nsec * NSEC_CONVERSION) >>(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
}

5.1. 秒部分转换

(u64)sec * SEC_CONVERSION

SEC_CONVERSION 的含义:

#define SEC_CONVERSION ((NSEC_PER_SEC << SEC_JIFFIE_SC) / TICK_NSEC)

实际意义:

  • NSEC_PER_SEC << SEC_JIFFIE_SC:将1秒的纳秒数放大 2^SEC_JIFFIE_SC 倍
  • / TICK_NSEC:除以每个tick的纳秒数
  • 结果:每秒钟对应的放大后的jiffies数

5.2. 纳秒部分转换

((u64)nsec * NSEC_CONVERSION) >> (NSEC_JIFFIE_SC - SEC_JIFFIE_SC)

NSEC_CONVERSION 的含义:

#define NSEC_CONVERSION ((1 << NSEC_JIFFIE_SC) / TICK_NSEC)

实际意义:

  • 1 << NSEC_JIFFIE_SC:将1纳秒放大 2^NSEC_JIFFIE_SC 倍
  • / TICK_NSEC:除以每个tick的纳秒数
  • 结果:每纳秒对应的放大后的jiffies数

缩放因子调整:

>> (NSEC_JIFFIE_SC - SEC_JIFFIE_SC)

因为:

  • NSEC_CONVERSION 使用 NSEC_JIFFIE_SC 缩放
  • SEC_CONVERSION 使用 SEC_JIFFIE_SC 缩放
  • 需要将纳秒部分的缩放调整到与秒部分相同的级别

5.3. 最终结果处理

>> SEC_JIFFIE_SC

作用: 去除缩放因子,得到真实的jiffies值

6. 设计原理深度解析

// 直接计算(可能溢出和精度丢失)
jiffies = (sec * NSEC_PER_SEC + nsec) / TICK_NSEC;

缩放方法的优势:

  1. 避免除法:使用预计算的常数,运行时只有乘法和移位,内核一般不允许用浮点运算
  2. 保持高精度:通过放大因子保留小数部分
  3. 防止溢出:合理选择缩放因子控制中间结果大小

缩放因子选择

#define SEC_JIFFIE_SC (31 - SHIFT_HZ)
#define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 29)

为什么是 +29?

  • 29 = log2(1,000,000,000) 的近似值
  • 因为纳秒到秒有109倍关系,229 接近这个数量级

四、jiffies时间和其他表示法转换jiffies_to_timespec

#define div_long_long_rem(dividend,divisor,remainder) \
({                                                      \u64 result = dividend;                          \*remainder = do_div(result,divisor);            \result;                                         \
})
static __inline__ void
jiffies_to_timespec(const unsigned long jiffies, struct timespec *value)
{/** Convert jiffies to nanoseconds and separate with* one divide.*/u64 nsec = (u64)jiffies * TICK_NSEC;value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec);
}

1. div_long_long_rem 宏定义

#define div_long_long_rem(dividend,divisor,remainder) \
({                                                      \u64 result = dividend;                          \*remainder = do_div(result,divisor);            \result;                                         \
})

这是一个同时返回商和余数的64位除法宏

u64 result = dividend;
  • 将被除数保存到临时变量 result
*remainder = do_div(result,divisor);
  • do_div(result, divisor):执行64位除法
    • 输入:result 作为被除数,divisor 作为除数
    • 操作:result = result / divisor,同时返回余数
    • 输出:余数存入 *remainder,商存入 result
result;
  • 返回商(result 现在包含除法结果)

2. jiffies_to_timespec 函数

static __inline__ void
jiffies_to_timespec(const unsigned long jiffies, struct timespec *value)

函数作用: 将jiffies转换为timespec结构体(秒 + 纳秒)

步骤1:计算总纳秒数

u64 nsec = (u64)jiffies * TICK_NSEC;
  • (u64)jiffies:将jiffies转换为64位,避免乘法溢出
  • TICK_NSEC:每个tick的纳秒数
  • 结果:nsec = 总纳秒数

步骤2:分离秒和纳秒部分

value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec);

参数:

  • nsec:总纳秒数(被除数)
  • NSEC_PER_SEC:1秒的纳秒数 = 1,000,000,000(除数)
  • &value->tv_nsec:余数(纳秒部分)的存储地址

执行过程:

  1. 计算 nsec / NSEC_PER_SEC 得到秒数
  2. 计算 nsec % NSEC_PER_SEC 得到纳秒部分
  3. 秒数存入 value->tv_sec
  4. 纳秒部分存入 value->tv_nsec

五、返回时间戳计数器get_cycles

typedef unsigned long long cycles_t;static inline cycles_t get_cycles (void)
{unsigned long long ret=0;#ifndef CONFIG_X86_TSCif (!cpu_has_tsc)return 0;
#endif#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC)rdtscll(ret);
#endifreturn ret;
}

第一部分:检查TSC支持

#ifndef CONFIG_X86_TSCif (!cpu_has_tsc)return 0;
#endif

条件: #ifndef CONFIG_X86_TSC

  • 如果内核配置没有启用TSC支持

执行逻辑:

if (!cpu_has_tsc)return 0;
  • cpu_has_tsc:CPU特性检测变量,检查当前CPU是否支持TSC
  • 如果CPU不支持TSC,直接返回0

第二部分:实际读取TSC

#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_XSC_TSC)rdtscll(ret);
#endif

条件: defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC)

  • 如果配置了通用x86支持明确启用了TSC支持

执行:

rdtscll(ret);
  • rdtscll:内联汇编宏,执行RDTSC指令读取时间戳计数器
  • 结果存入ret变量
    return ret;
  • 返回读取到的时间戳计数器值

六、将日期时间转换为时间戳mktime

static inline unsigned long
mktime (unsigned int year, unsigned int mon,unsigned int day, unsigned int hour,unsigned int min, unsigned int sec)
{if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */mon += 12;              /* Puts Feb last since it has leap day */year -= 1;}return ((((unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +year*365 - 719499)*24 + hour /* now have hours */)*60 + min /* now have minutes */)*60 + sec; /* finally seconds */
}

1. 函数定义和参数

static inline unsigned long
mktime (unsigned int year, unsigned int mon,unsigned int day, unsigned int hour,unsigned int min, unsigned int sec)
  • 将年月日时分秒转换为从某个纪元开始计算的秒数
  • 参数:年、月(1-12)、日、时、分、秒

2. 月份调整部分

    if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */mon += 12;              /* Puts Feb last since it has leap day */year -= 1;}

调整前: 1月=1, 2月=2, …, 12月=12
调整后: 3月=1, 4月=2, …, 2月=12

具体步骤:

mon -= 2  // 月份减2:1月→-1, 2月→0, 3月→1, ..., 12月→10

如果月份≤0(即1月或2月):

mon += 12  // -1→11, 0→12
year -= 1   // 把1月、2月算作前一年的13月、14月

为什么这样调整?

  • 将闰年敏感的2月放在一年最后
  • 简化闰年计算,因为现在从3月开始到次年2月结束,所以2月的天数直接加就好,不用算是28还是29

3. 核心计算公式

    return ((((unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +year*365 - 719499)*24 + hour /* now have hours */)*60 + min /* now have minutes */)*60 + sec; /* finally seconds */

3.1. 步骤1:计算天数

year/4 - year/100 + year/400 + 367*mon/12 + day + year*365 - 719499

这个公式计算从某个参考日期到目标日期的总天数

年份基础天数

year*365
  • 最基本的:每年按365天计算
  • 但这样没有考虑闰年,所以需要后面的修正

闰年修正

year/4 - year/100 + year/400

这是计算从公元1年到目标年份之间有多少个闰日的经典公式

year/4:每4年一个闰年

  • 年份1-4:1个闰年 → 1个闰日
  • 年份1-100:25个闰年 → 25个闰日

- year/100:减去世纪年(100的倍数不是闰年)

  • 年份1-100:减去1个(100年不是闰年)→ 24个闰日
  • 年份1-200:减去2个 → 48个闰日

+ year/400:加回400的倍数(是闰年)

  • 年份1-400:加回1个(400年是闰年)→ 97个闰日

月份天数计算

367*mon/12 + day
  • 367 / 12 约等于 30.58,每年平均月份天数约等于30.43
调整后月份实际月份每月天数累计天数(从3月1日开始)367×mon367×mon/12差值
13月3103673030
24月30317346130
35月316111019130
46月3092146812230
57月31122183515230
68月31153220218330
79月30184256921430
810月31214293624430
911月30245330327530
1012月31275367030530
111月31306403733630
122月28/29337440436730
  • 从表格可以看到367×mon/12 每月的计算结果和实际上每月的累计天数依次大于30,所以367×mon/12的计算结果减去30就是当月的累积的天数

纪元调整

- 719499

这个魔法数字将计算结果调整到Unix纪元(1970年1月1日)

719499的由来:

计算从公元1年1月1日到1970年1月1日的天数:

步骤1:1969个完整年的天数

1969*365 + (1969/4 - 1969/100 + 1969/400)
= 1969*365 + (492 - 19 + 4)
= 718,685 + 477
= 719,162

步骤2:调整到1970年1月1日
由于我们之前的月份计算从3月开始,需要额外调整:

  • 719162 + 367*11 / 12 + 1 = 719499 (实际上包含了差值30)

所以 - 719499 就是从公元1年调整到Unix纪元1970年,同时平衡了前面的差值30

3.2. 步骤2:逐级转换为秒数

(天数)*24 + hour    // 转换为小时
(小时数)*60 + min   // 转换为分钟  
(分钟数)*60 + sec   // 转换为秒数

七、以timeval格式返回当前时间do_gettimeofday

void do_gettimeofday(struct timeval *tv)
{unsigned long seq;unsigned long usec, sec;unsigned long max_ntp_tick;do {unsigned long lost;seq = read_seqbegin(&xtime_lock);usec = cur_timer->get_offset();lost = jiffies - wall_jiffies;/** If time_adjust is negative then NTP is slowing the clock* so make sure not to go into next possible interval.* Better to lose some accuracy than have time go backwards..*/if (unlikely(time_adjust < 0)) {max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;usec = min(usec, max_ntp_tick);if (lost)usec += lost * max_ntp_tick;}else if (unlikely(lost))usec += lost * (USEC_PER_SEC / HZ);sec = xtime.tv_sec;usec += (xtime.tv_nsec / 1000);} while (read_seqretry(&xtime_lock, seq));while (usec >= 1000000) {usec -= 1000000;sec++;}tv->tv_sec = sec;tv->tv_usec = usec;
}

1. 函数定义和变量声明

void do_gettimeofday(struct timeval *tv)
{unsigned long seq;unsigned long usec, sec;unsigned long max_ntp_tick;
  • 作用:获取当前时间,填充到timeval结构体(秒+微秒)
  • seq:顺序锁序列号,用于数据一致性保护
  • usec, sec:存储计算出的秒和微秒
  • max_ntp_tick:NTP调整时的最大tick值

2. 顺序锁保护的数据读取循环

    do {unsigned long lost;seq = read_seqbegin(&xtime_lock);
  • do-while循环:使用顺序锁确保读取到一致的时间数据
  • lost:记录丢失的tick数(中断延迟导致)
  • read_seqbegin():开始读取序列,获取当前序列号

3. 时间偏移量计算

        usec = cur_timer->get_offset();lost = jiffies - wall_jiffies;
  • cur_timer->get_offset():从硬件定时器获取当前tick内的微秒偏移量
  • lost = jiffies - wall_jiffieswall_jiffies墙上时钟不会每次时钟中断都更新

4. NTP时间调整处理

        if (unlikely(time_adjust < 0)) {max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;usec = min(usec, max_ntp_tick);if (lost)usec += lost * max_ntp_tick;}else if (unlikely(lost))usec += lost * (USEC_PER_SEC / HZ);

4.1. 情况1:NTP调慢时钟(time_adjust < 0)

max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;
usec = min(usec, max_ntp_tick);
  • 当系统时钟比真实时间快时,NTP需要调慢系统时钟,实现方式是让每个tick的时间变短
  • tickadj:每次tick的调整量
  • max_ntp_tick: 计算调整后的最大tick时间
  • min(usec, max_ntp_tick);: 限制当前tick内的时间不能超过调整后的最大值
if (lost)usec += lost * max_ntp_tick;
  • 为丢失的tick补偿时间,但使用调整后的tick长度
  • 这样补偿的时间也是"调慢后"的时间

4.2. 情况2:NTP调快或正常(time_adjust ≥ 0)但有丢失tick

else if (unlikely(lost))usec += lost * (USEC_PER_SEC / HZ);
  • 使用标准的tick长度进行补偿

5. 基础时间获取

        sec = xtime.tv_sec;usec += (xtime.tv_nsec / 1000);
  • xtime.tv_sec:从系统核心时间变量获取秒数
  • xtime.tv_nsec / 1000:将纳秒转换为微秒并累加

6. 一致性验证

    } while (read_seqretry(&xtime_lock, seq));
  • 检查在读取过程中是否有写操作修改了时间数据
  • 如果序列号变化,说明数据可能不一致,重新读取

7. 时间规范化

    while (usec >= 1000000) {usec -= 1000000;sec++;}

处理微秒溢出:如果微秒超过1秒,进位到秒

8. 结果输出

    tv->tv_sec = sec;tv->tv_usec = usec;
}
  • 将计算出的秒和微秒存入输出参数

八、以timespec格式返回当前时间current_kernel_time

struct timespec current_kernel_time(void)
{struct timespec now;unsigned long seq;do {seq = read_seqbegin(&xtime_lock);now = xtime;} while (read_seqretry(&xtime_lock, seq));return now;
}

1. 函数定义和变量声明

struct timespec current_kernel_time(void)
{struct timespec now;unsigned long seq;
  • 返回值struct timespec 包含秒和纳秒的时间结构体
  • 作用:安全地获取内核维护的当前时间
  • now:用于存储读取到的时间值
  • seq:顺序锁的序列号,用于一致性检查

2. 顺序锁保护的数据读取循环

    do {seq = read_seqbegin(&xtime_lock);now = xtime;} while (read_seqretry(&xtime_lock, seq));

这是典型的读-复制-验证模式,确保读取到一致的时间数据

步骤1:开始读取序列

seq = read_seqbegin(&xtime_lock);
  • read_seqbegin():读取顺序锁的当前序列号
  • 如果序列号是偶数,表示没有写操作在进行
  • 如果序列号是奇数,表示有写操作正在进行

步骤2:读取时间数据

now = xtime;
  • xtime:内核维护的当前时间变量(struct timespec类型)
  • 这是一个结构体拷贝,不是指针引用

步骤3:验证数据一致性

while (read_seqretry(&xtime_lock, seq));
  • 检查序列号是否与开始时相同
  • 如果不同,说明在我们读取过程中有写操作发生,需要重新读取

3. 返回值

    return now;
}
  • 返回读取到的时间结构体副本

4. 与其他时间函数的区别

4.1. vs do_gettimeofday()

// current_kernel_time: 返回内核维护的粗略时间
// do_gettimeofday: 返回高精度时间current_kernel_time() → 直接返回xtime
do_gettimeofday() → xtime + NTP调整 + 丢失tick补偿
http://www.dtcms.com/a/461351.html

相关文章:

  • InterGEO2025 | 和芯星通发布UM98XC系列 全系统多频高精度RTK星基定位模块
  • Node.js 工具模块详解
  • k8s介绍和特性
  • 上海网站建设推网络营销方式整理
  • 软软一键开关 --提供多个 Windows 系统开关,例如保持常亮、隐藏桌面图标、显示器亮度、夜间模式等
  • C 数组:深入解析与高效应用
  • 牛客网_动态规划
  • 《边缘端工业系统的编程优化与性能突破》
  • Typescript中的Type check类型检查
  • 【2063】牛吃牧草
  • 网站开发专业优势吉林长春建设工程信息网站
  • 16. SPDK应用框架
  • 【2026计算机毕业设计】基于Jsp的校园勤工俭学兼职系统
  • ⸢ 柒-Ⅱ⸥ ⤳ 可信纵深防御建设方案:应用可信网络可信
  • 做棋牌游戏网站犯法吗网站建设公司咋样
  • 自己做网站怎么加定位seo首页排名优化
  • 华为NetEngine 8000 M1A路由器配置
  • 【C/C++】一篇小文速通 数据类型
  • 棱镜观察|极氪销量遇阻?千里智驾左手服务吉利、右手对标华为
  • 如何安全轻松地出售损坏的 iPhone(最新指南)
  • QT-常用控件(二)
  • React 源码揭秘 | 合成事件
  • 如何处理旧 iPhone:安全地回收或重新利用
  • 过年做那些网站能致富网页制作培训苏州
  • 公司网站百度地图微信小程序商城源代码
  • 【征文计划】AI+AR生态新未来,Rokid核心技术实战解析
  • AI 驱动的 AR眼镜巡检技术方案:让工业缺陷识别更精准高效|阿法龙XR云平台​
  • JFM9VU3P开发板/国产FPGA/ QSFP+ 40G 光纤接口
  • 使用as断言可能会掩盖类型错误,更安全的方式是:
  • 安宝特方案丨软硬件双升级的AR智能仓储物流解决方案