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

UNIX下C语言编程与实践39-UNIX 定时器:alarm 函数与 setitimer 函数的使用与对比

从秒级普通定时到毫秒级精确定时,掌握 UNIX 系统定时机制的核心工具

一、UNIX 定时器的核心定位与分类

在 UNIX 系统中,定时器(Timer)是实现“定时触发事件”的核心机制,其本质是“内核在指定时间后向进程发送特定信号”,进程通过捕获信号执行预设逻辑(如定时任务、超时处理)。根据精度和功能差异,UNIX 定时器主要分为两类:

  • 普通定时器(alarm 函数):秒级精度,仅支持“一次定时”或“覆盖式定时”,功能简单,适用于对精度要求不高的场景(如超时提醒、简单定时任务);
  • 精确定时器(setitimer 函数):理论毫秒级精度,支持三种计时类型(真实时间、用户态时间、用户态+核心态时间)和“重复定时”,功能灵活,适用于高精度、复杂定时场景(如性能监控、高频任务调度)。

两种定时器均依赖“信号触发”机制(如 SIGALRMSIGVTALRM),进程需通过信号捕获函数响应定时事件,本质是“异步定时”——定时器触发不阻塞进程主流程,而是通过信号中断实现。

二、普通定时器:alarm 函数的使用

alarm 函数是 UNIX 系统中最基础的定时接口,定义在 <unistd.h> 头文件中,核心功能是“在指定秒数后向进程发送 SIGALRM 信号”,仅支持秒级精度和简单定时逻辑。

1. 函数原型与核心参数

#include <unistd.h>// 功能:设置秒级定时器,指定秒数后发送 SIGALRM 信号
// 参数:
//   seconds:定时秒数,取值 >= 0;
//     - 若 seconds > 0:设置新定时器,seconds 秒后触发;
//     - 若 seconds = 0:取消当前已设置的 alarm 定时器。
// 返回值:
//   - 成功:返回之前未到期的定时器剩余秒数(若无可返回 0);
//   - 失败:返回 -1(极少发生,通常因参数无效)。
unsigned int alarm(unsigned int seconds);

关键特性

  • 覆盖性:若进程已设置未到期的 alarm 定时器,再次调用 alarm(seconds) 会覆盖原定时器,返回原定时器的剩余秒数;
  • 一次性:定时器触发(发送 SIGALRM)后自动失效,若需重复定时,需在信号捕获函数中重新调用 alarm
  • 信号依赖:定时器触发的核心是 SIGALRM 信号,若进程忽略或未捕获该信号,默认行为是终止进程(需特别注意)。

2. 实战:alarm 函数的典型用法

实例 1:基础定时——3 秒后触发 SIGALRM 信号

以下为规范格式后的代码和说明:

代码部分

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>// SIGALRM 信号捕获函数
void sigalrm_handler(int sig) {printf("捕获到 SIGALRM 信号(定时时间到),程序退出\n");exit(EXIT_SUCCESS);
}int main() {// 1. 捕获 SIGALRM 信号(避免默认终止)if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {perror("signal 注册 SIGALRM 失败");return 1;}// 2. 设置 3 秒后触发定时器unsigned int remaining = alarm(3);printf("已设置 3 秒定时器,之前无未到期定时器,返回值:%u\n", remaining);// 3. 主进程阻塞等待(避免提前退出)printf("主进程等待定时器触发...\n");while (1) {sleep(1);printf("等待中...\n");}return 0;
}

编译与运行

# 1. 编译程序
gcc alarm_basic.c -o alarm_basic# 2. 运行程序
./alarm_basic

预期输出

已设置 3 秒定时器,之前无未到期定时器,返回值:0
主进程等待定时器触发...
等待中...
等待中...
等待中...
捕获到 SIGALRM 信号(定时时间到),程序退出

实例 2:重复定时——每隔 1 秒触发一次 SIGALRM
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>// 全局计数:记录定时触发次数(volatile 避免编译器优化)
volatile int count = 0;// SIGALRM 捕获函数:重新设置定时器实现重复定时
void sigalrm_handler(int sig) {count++;printf("第 %d 次定时触发(每隔 1 秒)\n", count);// 触发 5 次后退出if (count >= 5) {printf("已触发 5 次,程序退出\n");exit(EXIT_SUCCESS);}// 重新设置 1 秒定时器,实现重复触发alarm(1);
}int main() {// 注册 SIGALRM 捕获函数if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {perror("signal 注册失败");return 1;}// 首次设置 1 秒定时器alarm(1);printf("重复定时器已启动,每隔 1 秒触发一次(共 5 次)\n");// 主进程循环等待while (1) {pause(); // 阻塞等待信号,比 sleep 更高效}return 0;
}

编译与运行

gcc alarm_repeat.c -o alarm_repeat
./alarm_repeat

预期输出

重复定时器已启动,每隔 1 秒触发一次(共 5 次)
第 1 次定时触发(每隔 1 秒)
第 2 次定时触发(每隔 1 秒)
第 3 次定时触发(每隔 1 秒)
第 4 次定时触发(每隔 1 秒)
第 5 次定时触发(每隔 1 秒)
已触发 5 次,程序退出

关键技巧:使用 pause() 替代 sleep(1) 等待信号,pause() 会一直阻塞直到收到任意信号,避免 sleep 的时间误差,更适合信号驱动的场景。

三、精确定时器:setitimer 函数的使用

setitimer 函数是 UNIX 系统提供的增强型定时接口,定义在 <sys/time.h> 头文件中。相比 alarm 函数,它支持毫秒级精度、三种计时类型和自动重复定时,功能更强大,适用于高精度定时场景。

1. 函数原型与核心参数

#include <sys/time.h>// 功能:设置精确定时器,支持三种类型和重复定时
// 参数:
//   which:定时器类型(ITIMER_REAL / ITIMER_VIRT / ITIMER_PROF);
//   new_value:新定时器参数(定时时间、重复间隔);
//   old_value:非 NULL 时,存储之前定时器的参数(用于恢复);
// 返回值:成功返回 0,失败返回 -1,错误码存入 errno。
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);// 定时器参数结构体:包含“初始定时时间”和“重复间隔时间”
struct itimerval {struct timeval it_interval; // 重复定时的间隔时间(0 表示仅一次)struct timeval it_value;    // 首次定时的时间(0 表示取消定时器)
};// 时间结构体:支持秒和微秒(理论精度 1 微秒,实际受系统调度影响)
struct timeval {time_t      tv_sec;      // 秒数suseconds_t tv_usec;     // 微秒数(0 ~ 999999)
};

核心概念解析

  • struct itimerval 的作用:
    • it_value:首次触发定时器的时间(如 {1, 500000} 表示 1.5 秒后首次触发);
    • it_interval:首次触发后,重复定时的间隔时间(如 {1, 500000} 表示每隔 1.5 秒重复触发;为 {0, 0} 表示仅触发一次)。
  • 取消定时器:将 new_value->it_value 设置为 {0, 0},调用 setitimer 即可取消对应类型的定时器。

2. setitimer 的三种定时器类型

setitimer 支持三种不同的计时类型,对应不同的时间统计范围和触发信号,适用于不同场景:

定时器类型计时范围触发信号核心用途精度影响因素
ITIMER_REAL真实时间(墙钟时间),无论进程是否运行,时间都会累积SIGALRM通用定时场景(如超时提醒、定时任务调度),与 alarm 函数功能重叠系统负载(高负载时,信号可能延迟送达),理论精度 1 微秒,实际约 10~100 微秒
ITIMER_VIRT进程在用户态运行的时间(仅统计进程执行用户代码的时间,内核态时间不累积)SIGVTALRM用户态代码性能监控(如统计函数执行耗时、限制用户态运行时间)进程调度(仅进程在用户态时计时),精度较高,不受其他进程影响
ITIMER_PROF进程在用户态 + 核心态运行的总时间(统计进程占用 CPU 的所有时间)SIGPROF进程总 CPU 占用监控(如性能分析、限制进程总 CPU 时间)CPU 调度(仅进程占用 CPU 时计时),精度与 ITIMER_VIRT 相当

3. 实战:setitimer 函数的典型用法

实例 1:ITIMER_REAL 类型——1.5 秒重复定时(真实时间)
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>volatile int count = 0;// SIGALRM 捕获函数(ITIMER_REAL 触发)
void sigalrm_handler(int sig) {count++;printf("第 %d 次定时触发(ITIMER_REAL,间隔 1.5 秒)\n", count);if (count >= 3) {printf("已触发 3 次,取消定时器并退出\n");// 取消 ITIMER_REAL 定时器struct itimerval cancel_timer = {{0, 0}, {0, 0}};setitimer(ITIMER_REAL, &cancel_timer, NULL);exit(EXIT_SUCCESS);}
}int main() {// 1. 捕获 SIGALRM 信号if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {perror("signal 注册失败");return 1;}// 2. 配置定时器参数:首次 1.5 秒,重复间隔 1.5 秒struct itimerval timer;// 重复间隔:1 秒 + 500000 微秒 = 1.5 秒timer.it_interval.tv_sec = 1;timer.it_interval.tv_usec = 500000;// 首次定时:1.5 秒timer.it_value.tv_sec = 1;timer.it_value.tv_usec = 500000;// 3. 设置 ITIMER_REAL 定时器if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {perror("setitimer 失败");return 1;}printf("ITIMER_REAL 定时器已启动(1.5 秒重复)\n");// 4. 等待信号while (1) {pause();}return 0;
}

# 1. 编译程序
gcc setitimer_real.c -o setitimer_real# 2. 运行程序
./setitimer_real

输出结果

ITIMER_REAL 定时器已启动(1.5 秒重复)
第 1 次定时触发(ITIMER_REAL,间隔 1.5 秒)
第 2 次定时触发(ITIMER_REAL,间隔 1.5 秒)
第 3 次定时触发(ITIMER_REAL,间隔 1.5 秒)
已触发 3 次,取消定时器并退出

实例 2:ITIMER_VIRT 类型——用户态 0.5 秒定时(仅统计用户态时间)
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>volatile int user_time_count = 0;// SIGVTALRM 捕获函数(ITIMER_VIRT 触发)
void sigvtalrm_handler(int sig) {user_time_count++;printf("用户态时间累积 0.5 秒,触发第 %d 次信号\n", user_time_count);if (user_time_count >= 2) {printf("用户态总时间已达 1 秒,程序退出\n");exit(EXIT_SUCCESS);}
}int main() {// 1. 捕获 SIGVTALRM 信号if (signal(SIGVTALRM, sigvtalrm_handler) == SIG_ERR) {perror("signal 注册失败");return 1;}// 2. 配置 ITIMER_VIRT 定时器:用户态每累积 0.5 秒触发struct itimerval timer;timer.it_interval = (struct timeval){0, 500000}; // 重复间隔 0.5 秒timer.it_value = (struct timeval){0, 500000};    // 首次 0.5 秒if (setitimer(ITIMER_VIRT, &timer, NULL) == -1) {perror("setitimer 失败");return 1;}printf("ITIMER_VIRT 定时器已启动(用户态每 0.5 秒触发)\n");// 3. 执行用户态计算(模拟用户态耗时操作)printf("开始执行用户态计算...\n");unsigned long long sum = 0;while (1) {sum++; // 纯用户态操作,累积用户态时间}return 0;
}

编译与运行命令

gcc setitimer_virt.c -o setitimer_virt
./setitimer_virt

预期输出

ITIMER_VIRT 定时器已启动(用户态每 0.5 秒触发)
开始执行用户态计算...
用户态时间累积 0.5 秒,触发第 1 次信号
用户态时间累积 0.5 秒,触发第 2 次信号
用户态总时间已达 1 秒,程序退出

关键结论:ITIMER_VIRT 仅统计用户态时间——若进程因 I/O 阻塞进入睡眠(非用户态),计时会暂停;只有进程执行用户代码时,时间才会累积,适合精准统计用户态代码的运行时长。

四、alarm 与 setitimer 的对比与相互影响

alarm 和 setitimer(尤其是 ITIMER_REAL 类型)在功能上存在重叠,但在精度、灵活性和适用场景上差异显著。同时,两者存在相互影响,使用时需特别注意避免冲突。

1. 核心功能对比

对比维度alarm 函数setitimer 函数(ITIMER_REAL)setitimer 函数(ITIMER_VIRT/ITIMER_PROF)
精度秒级(最低 1 秒)毫秒级(理论 1 微秒,实际 10~100 微秒)毫秒级
定时类型仅真实时间(墙钟时间)真实时间用户态时间 / 用户态+核心态时间
重复定时需手动在信号处理函数中重新调用支持自动重复(通过 it_interval 设置)支持自动重复
触发信号SIGALRMSIGALRMSIGVTALRM / SIGPROF
参数复杂度简单(仅需秒数)复杂(需配置 struct itimerval)复杂
适用场景简单秒级定时(如超时提醒、基础定时任务)高精度真实时间定时(如高频任务调度、精准超时控制)进程 CPU 时间监控(如性能分析、CPU 时间限制)
优点使用简单、代码侵入性低精度高、支持自动重复、功能灵活精准统计 CPU 时间、不受系统负载影响
缺点精度低、不支持自动重复参数复杂、受系统负载影响精度仅统计 CPU 时间、不适合真实时间定时

2. 相互影响:不可同时使用的场景

核心冲突:alarm 与 ITIMER_REAL 共享 SIGALRM 信号

alarm 函数和 setitimer 的 ITIMER_REAL 类型均通过 SIGALRM 信号触发,且共享同一内核定时器资源——若进程同时使用两者,会产生以下冲突:

  • 覆盖冲突:调用 alarm(seconds) 会覆盖已设置的 ITIMER_REAL 定时器,反之亦然;例如,先通过 setitimer 设置 ITIMER_REAL 定时,再调用 alarm(5),原 ITIMER_REAL 定时器会被取消;
  • 信号混淆SIGALRM 信号触发后,进程无法区分是来自 alarm 还是 ITIMER_REAL,可能导致逻辑错误;
  • 精度丢失:alarm 是秒级精度,会降低 ITIMER_REAL 的毫秒级精度优势。

解决方法:同一进程中,禁止同时使用 alarm 函数和 ITIMER_REAL 类型的 setitimer;若需高精度定时,优先使用 setitimer(ITIMER_REAL);若需简单定时,使用 alarm,二者择一。

无冲突场景:alarm 与 ITIMER_VIRT/ITIMER_PROF 无冲突——这两种 setitimer 类型分别触发 SIGVTALRM 和 SIGPROF 信号,与 alarm 的 SIGALRM 信号独立,可同时使用。

冲突演示:alarm 覆盖 ITIMER_REAL 定时器
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>void sigalrm_handler(int sig) {printf("捕获到 SIGALRM 信号(来自 alarm,而非 ITIMER_REAL)\n");exit(EXIT_SUCCESS);
}int main() {signal(SIGALRM, sigalrm_handler);// 1. 先设置 ITIMER_REAL 定时器(1.5 秒触发)struct itimerval timer = {{0, 0},         // 仅一次定时,无重复{1, 500000}     // 1.5 秒后触发};if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {perror("setitimer 失败");return 1;}printf("已设置 ITIMER_REAL 定时器(1.5 秒后触发)\n");// 2. 1 秒后调用 alarm(2),覆盖 ITIMER_REALsleep(1);alarm(2);printf("1 秒后调用 alarm(2),覆盖 ITIMER_REAL 定时器\n");// 等待信号while (1) {pause();}return 0;
}

已设置 ITIMER_REAL 定时器(1.5 秒后触发)
1 秒后调用 alarm(2),覆盖 ITIMER_REAL 定时器
捕获到 SIGALRM 信号(来自 alarm,而非 ITIMER_REAL)

结论:ITIMER_REAL 定时器被 alarm 覆盖,最终触发的 SIGALRM 来自 alarm,证明二者存在冲突,不可同时使用。

五、定时器使用的常见错误与解决方法

在使用 alarm 或 setitimer 时,易因参数配置、信号处理或类型选择错误导致定时失效、程序崩溃等问题。以下是高频错误及解决方法:

常见错误问题现象原因分析解决方法
未捕获定时器信号,程序被终止定时器触发后,程序无任何提示直接退出,终端显示“Terminated”定时器触发的信号(如 SIGALRM、SIGVTALRM)默认处理动作是“终止进程”,若进程未捕获或忽略这些信号,会被系统终止1. 必须通过 signal 或 sigaction 注册信号捕获函数,自定义信号处理逻辑;
2. 示例:
if (signal(SIGALRM, handler) == SIG_ERR) { perror("signal 失败"); }
3. 若无需处理信号,可忽略信号(不推荐,可能丢失定时事件):
signal(SIGALRM, SIG_IGN);
setitimer 参数结构体初始化错误定时器未按预期触发(如定时时间偏差大、不重复触发),或 setitimer 调用失败1. 未初始化 struct itimerval 或 struct timeval,存在垃圾数据(如 tv_usec 为负数或超过 999999);
2. 混淆 it_interval 和 it_value 的作用(如将重复间隔设为 it_value,导致仅触发一次);
3. tv_usec 取值超出范围(应在 0~999999 之间)
1. 显式初始化结构体,避免垃圾数据:
struct itimerval timer = {{1, 0}, {2, 0}};(推荐)或 memset(&timer, 0, sizeof(timer));
2. 明确参数含义:
- it_value:首次触发时间(必须设置,否则定时器不生效);
- it_interval:重复间隔(0 表示仅一次);
3. 确保 tv_usec 取值合法(0 ≤ tv_usec ≤ 999999),避免整数溢出
alarm 重复定时时未重新调用alarm 定时器仅触发一次,后续无信号触发alarm 是“一次性”定时器,触发后自动失效;若需重复定时,需在信号捕获函数中重新调用 alarm(seconds),否则仅触发一次在 SIGALRM 信号捕获函数中重新设置 alarm,实现重复定时:
void handler(int sig) { alarm(1); /* 其他逻辑 */ }
注意:重新调用的时间应与预期间隔一致,避免累积误差
定时器精度低于预期(如毫秒级定时偏差大)setitimer(ITIMER_REAL) 设置 100ms 定时,实际触发间隔为 120~150ms,偏差超过 20%1. 系统负载过高:高负载时,内核调度延迟,信号无法及时送达;
2. 进程阻塞:进程因 I/O、sleep 等操作阻塞,错过信号处理时机;
3. 硬件精度限制:部分嵌入式系统或老旧硬件的时钟精度不足,无法支持毫秒级定时
1. 降低系统负载:关闭无用进程,减少资源竞争;
2. 避免进程长时间阻塞:使用非阻塞 I/O,减少 sleep 调用;
3. 选择合适的定时类型:若需精准 CPU 时间统计,改用 ITIMER_VIRT/ITIMER_PROF(不受系统负载影响);
4. 硬件层面优化:在嵌入式系统中,选择高精度时钟硬件(如 RTC 实时时钟)
同时使用 alarm 和 ITIMER_REAL,导致定时混乱定时器触发时间与预期不符,或信号处理逻辑错乱(无法区分信号来源)alarm 和 ITIMER_REAL 共享 SIGALRM 信号和内核定时器资源,同时使用会相互覆盖,导致定时逻辑混乱1. 同一进程中禁止同时使用 alarm 和 ITIMER_REAL,二者择一;
2. 若需高精度定时,优先使用 setitimer(ITIMER_REAL);若需简单秒级定时,使用 alarm;
3. 若需同时处理真实时间和 CPU 时间定时,使用 setitimer(ITIMER_REAL) + setitimer(ITIMER_VIRT),避免使用 alarm

六、定时器的实际应用场景

定时器在 UNIX 程序开发中应用广泛,尤其在服务端程序、性能监控、嵌入式系统中,是实现定时任务、超时控制的核心工具。以下是典型应用场景:

1. 服务端程序的超时控制

服务端程序(如 Web 服务器、数据库)在处理客户端请求时,需设置超时时间(如 30 秒无响应则断开连接),避免因客户端异常导致资源长期占用。

实现方案

  • 使用 setitimer(ITIMER_REAL, ...) 设置超时时间(如 30 秒);
  • 客户端请求处理完成前,若收到 SIGALRM 信号,判定为超时,关闭连接并释放资源;
  • 请求正常处理完成后,取消定时器(设置 it_value 为 {0, 0})。

优势:ITIMER_REAL 的毫秒级精度可实现精准超时控制,避免误判;自动重复定时可简化多请求场景的超时管理。

2. 定时任务调度

系统工具或应用程序需定期执行任务(如日志轮转、数据备份、状态上报),需通过定时器实现周期性触发。

实现方案

  • 简单场景(秒级间隔):使用 alarm 函数,在 SIGALRM 捕获函数中重新调用 alarm,实现重复定时;
  • 复杂场景(毫秒级间隔、多任务):使用 setitimer(ITIMER_REAL),通过不同信号(如 SIGALRM、SIGVTALRM)区分不同定时任务;
  • 示例:日志系统每隔 1 小时轮转日志,通过 setitimer 设置 ITIMER_REAL 定时,触发后执行日志重命名和压缩逻辑。

优势:无需额外线程,通过信号异步触发,不阻塞主流程,资源占用低。

3. 性能监控与分析

性能分析工具(如 gprof)需统计程序的 CPU 时间占用(用户态、核心态),或定期采样程序运行状态,用于定位性能瓶颈。

实现方案

  • 统计用户态时间:使用 setitimer(ITIMER_VIRT, ...),定时触发 SIGVTALRM 信号,记录当前执行的函数栈;
  • 统计总 CPU 时间:使用 setitimer(ITIMER_PROF, ...),同时记录用户态和核心态时间占比;
  • 示例:性能工具每隔 10ms 采样一次函数调用栈,通过 ITIMER_VIRT 定时实现,生成 CPU 占用热力图。

优势:ITIMER_VIRT/ITIMER_PROF 精准统计 CPU 时间,不受系统负载影响,是性能分析的核心工具。

4. 嵌入式系统的实时控制

嵌入式系统(如工业控制、智能家居)需实时响应外部事件(如传感器数据采集、设备控制),需毫秒级甚至微秒级定时。

实现方案

  • 高精度定时:使用 setitimer(ITIMER_REAL, ...),结合硬件时钟,实现 10~100 微秒级定时;
  • 任务调度:通过多个定时器(如 ITIMER_REAL 用于设备控制,ITIMER_VIRT 用于数据处理),实现多任务并发调度;
  • 示例:温度传感器每隔 500ms 采集一次数据,通过 setitimer(ITIMER_REAL) 定时触发采集逻辑。

优势:setitimer 的高精度和灵活类型,满足嵌入式系统的实时性需求,资源占用低,适合资源受限的场景。

UNIX 系统中两种核心定时器(alarm 和 setitimer)的使用方法、参数配置、类型差异,对比了二者的优缺点和适用场景,分析了常见错误与解决方法,并介绍了实际应用场景。alarm 函数简单易用,适合秒级普通定时;setitimer 函数功能强大,支持毫秒级精度和多种计时类型,适合高精度、复杂定时场景。

在实际开发中,需根据需求选择合适的定时器:

  • 简单秒级定时(如超时提醒)→ 选择 alarm 函数,代码简洁;
  • 高精度真实时间定时(如服务端超时控制)→ 选择 setitimer(ITIMER_REAL);
  • CPU 时间统计(如性能监控)→ 选择 setitimer(ITIMER_VIRT/ITIMER_PROF);
  • 禁止同时使用 alarm 和 ITIMER_REAL,避免信号和定时器冲突。

掌握定时器的使用,是编写高效、健壮 UNIX 程序的基础,尤其在服务端开发和嵌入式领域,定时器是实现定时任务、超时控制、性能监控的核心工具。

http://www.dtcms.com/a/449623.html

相关文章:

  • 18款禁用网站app全部用asp.net 做网站
  • 对比网站中国最大免费wap网站
  • 虚拟麦克风驱动下载
  • 算法题(227):回文字符串
  • 框架--SpringBoot
  • STM32F103 MPU6500 DMP库姿态解算
  • 使用第三方库
  • 腾讯企业邮箱登录入口app云优化seo软件
  • 【操作系统-Day 40】文件的“身份证”:深入解析文件定义、属性与核心操作
  • 磁共振成像原理(理论)19:基本成像原理 (Basic Imaging Methods) - 三维成像
  • 线程池——线程池
  • WebSocket细谈
  • 公司网站怎么建站微网站如何做微信支付宝支付
  • Ubuntu 原地升级 MongoDB 全攻略
  • 网站变灰色代码安徽省建设工程信息网官网是什么网站
  • Hexo博客搭建系列(四):透明居中导航栏+鼠标悬停放大效果
  • 【STM32项目开源】基于STM32的智能仓库火灾检测系统
  • 陕西省建设监理协会网站证书wordpress 图片外链
  • 做模板网站企业网站类型
  • 24H2壁纸显示错误修复(针对vb.net的紧急加更)
  • 兰州做网站 东方商易怎么样做美术招生信息网站
  • 酒店客房管理系统|基于SpringBoot和Vue的酒店客房管理系统(源码+数据库+文档)
  • AI编程开发系统019-基于Vue+SpringBoot的邮件收发系统(源码+部署说明+演示视频+源码介绍+lw)
  • 做海免费素材网站排版设计模板
  • 212-基于Python的老人健康管理系统
  • 万能格式文件查看工具,支持查看图像、音视频和文档等,免安装超方便!
  • 做食品企业网站的费用wordpress文章图片全屏浏览
  • 韩国免费行情网站的推荐理由外贸 wordpress
  • 嵌入式开发核心知识点详解教程
  • 操作系统应用开发(二十六)RustDesk tls证书不匹配错误—东方仙盟筑基期