西安正规网站建设报价/免费软文网站
用户空间定时器与内核定时器的关系
虽然用户空间定时器和内核定时器在实现上各自独立,但用户空间定时器通常依赖于内核定时器提供的基础设施。以下是具体关系:
- 依赖性
- 用户空间定时器的实现基于内核定时器。
- 例如,POSIX 定时器使用内核的
hrtimer
机制。 alarm()
和setitimer()
使用内核低精度定时器。
- 例如,POSIX 定时器使用内核的
- 内核通过用户态 API(如
timerfd
和clock_nanosleep()
)将定时器功能暴露给用户空间。
- 用户空间定时器的实现基于内核定时器。
- 精度
- 用户空间定时器的精度取决于内核定时器的支持:
- 在启用高分辨率定时器(
CONFIG_HIGH_RES_TIMERS=y
)时,用户空间定时器可以实现亚毫秒级精度。 - 如果系统只支持低分辨率定时器,用户空间定时器的精度会受到限制(分辨率基于
HZ
和jiffies
)。
- 在启用高分辨率定时器(
- 用户空间定时器的精度取决于内核定时器的支持:
- 隔离性
- 内核定时器运行在内核态,直接管理硬件资源,对用户态透明。
- 用户空间定时器运行在用户态,通过系统调用访问内核资源。
- 性能
- 内核定时器的执行效率更高,因为它直接操作硬件并在内核上下文中运行。
- 用户空间定时器的执行效率相对较低,因其需要从用户态切换到内核态并依赖系统调用。
用户空间定时器
在 Linux 中,用户空间有多种方式可以使用定时器来执行定时任务或管理延时操作。以下是常见的方法及其详细说明:
1. 使用 **alarm()**
函数
- 功能:
alarm()
用于设置一个定时器,定时结束时会向当前进程发送SIGALRM
信号。
- 用法:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>void alarm_handler(int sig) {printf("Alarm triggered!\n");
}int main() {signal(SIGALRM, alarm_handler); // 设置信号处理函数alarm(5); // 设置定时器,5秒后触发pause(); // 等待信号return 0;
}
- 特点:
- 简单易用。
- 只能设置秒级的定时器。
- 只能设置一个定时器。
- 定时器到期后,如果是多线程,会选择一个当前非阻塞的线程来处理信号,处理线程并不确定。
- 依赖的内核机制
- 依赖于**内核 jiffies,**即低分辨率定时器。
- 内核定时器队列(
timer_list
)会管理这些定时器,触发时发送信号。
2. 使用 **setitimer()**
函数
- 功能:
setitimer()
可以设置定时器,以精确控制一次性或周期性的定时任务。- 当定时时间到达时,会向调用进程发送信号(通常是
SIGALRM
)。
- 用法:
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>void timer_handler(int sig) {printf("Timer triggered!\n");
}int main() {struct itimerval timer;signal(SIGALRM, timer_handler); // 设置信号处理函数// 定时器间隔为1秒timer.it_value.tv_sec = 1;timer.it_value.tv_usec = 0;timer.it_interval.tv_sec = 1;timer.it_interval.tv_usec = 0;setitimer(ITIMER_REAL, &timer, NULL); // 设置定时器while (1); // 无限循环等待定时器触发return 0;
}
- 特点:
- 支持周期性定时器。
- 提供微秒级别的精度。
- 依赖的内核机制
- 本身最初是设计为基于低分辨率定时器,但在现代内核中,如果高分辨率定时器启用,它可以利用高分辨率定时器来提高精度。
- 内核定时器队列(
timer_list
)会管理这些定时器,触发时发送信号。
3. 使用 POSIX 定时器 (**timer_create()**
** 和 **timer_settime()**
)**
- 功能:
- POSIX 定时器提供了灵活的定时功能,可以通过多种方式通知(如信号或回调)。
- 可以创建多个定时器。
- 使用
timer_create()
创建一个定时器,时间到达时触发信号或调用特定的回调函数。
- 用法:
**timer_create**
:- 用于创建一个 POSIX 定时器。
- 可以选择通知方式,包括发送信号(
SIGEV_SIGNAL
)和使用线程回调(SIGEV_THREAD
)。
**timer_settime**
:- 用于设置定时器的到期时间 (
it_value
) 和周期性时间 (it_interval
)。
- 用于设置定时器的到期时间 (
- 定时器的通知方式:
SIGEV_SIGNAL
: 定时器超时时发送指定信号(如SIGALRM
)。SIGEV_THREAD
: 定时器超时时调用用户定义的回调函数。
// gcc -o posix_timer posix_timer.c -lrt -pthread#include <time.h>
#include <signal.h>
#include <stdio.h>void timer_handler(union sigval sv) {printf("POSIX timer triggered![%ld]\n", pthread_self());
}int main() {timer_t timerid;struct sigevent sev;struct itimerspec ts;sev.sigev_notify = SIGEV_THREAD; // 使用线程通知方式sev.sigev_value.sival_ptr = &timerid;sev.sigev_notify_function = timer_handler;sev.sigev_notify_attributes = NULL;timer_create(CLOCK_REALTIME, &sev, &timerid);ts.it_value.tv_sec = 2; // 初始触发时间ts.it_value.tv_nsec = 0;ts.it_interval.tv_sec = 2; // 周期触发时间ts.it_interval.tv_nsec = 0;timer_settime(timerid, 0, &ts, NULL);while (1); // 等待定时器触发return 0;
}
可以看到有两个线程,一个main线程,一个回调线程。
$ #ps -eLf | grep posix_timer
congchp 1965725 1138860 1965725 99 2 11:11 pts/4 00:02:27 ./posix_timer
congchp 1965725 1138860 1965726 0 2 11:11 pts/4 00:00:00 ./posix_timer
congchp 1966341 939085 1966341 0 1 11:13 pts/2 00:00:00 grep --color=auto posix_timer
- 特点:
- 支持多个定时器。
- 支持多种通知方式(如线程回调或信号)。
- 通知方式如果使用SIGEV_THREAD,则自动创建一个回调线程。
- 依赖的内核机制
- 内核使用
hrtimer
(高精度定时器)实现。 hrtimer
基于内核的高精度时间子系统,支持纳秒级别的精度。- 事件触发通过
softirq
或中断机制实现。
- 内核使用
4. 使用 **sleep()**
**nanosleep()**``**clock_nanosleep()**
函数
- 功能
- 这些函数用于让进程挂起一定时间。
sleep()
提供秒级精度,而nanosleep()
和clock_nanosleep()
提供纳秒级精度。- 可以被信号(如Ctrl+C)打断。
※Ctrl+C后,先发送SIGINT
,如果SIGINT
没有被处理,则发送SIGKILL
强制终止进程。
- 用法:
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>void handle_sigint(int sig) {printf("Received SIGINT (Ctrl+C). Exiting...\n");
}int main() {struct timespec req, rem;req.tv_sec = 2; // Sleep for 2 secondsreq.tv_nsec = 500000000; // Plus 500 milliseconds// 设置自定义信号处理函数signal(SIGINT, handle_sigint);printf("Sleeping for 2.5 seconds...\n");if (nanosleep(&req, &rem) == -1) {perror("nanosleep interrupted");printf("%ld.%ld remain\n", rem.tv_sec, rem.tv_nsec);return 1;}printf("Sleep complete!\n");return 0;
}
- 依赖的内核机制
- 使用hrtimer(高精度定时器)进行时间跟踪。
- 内核会将进程标记为睡眠状态,定时时间到后通过唤醒机制让进程继续运行。
5. **使用 ****poll()**
/select()
/epoll()
的超时功能
- 功能
- 提供 I/O 多路复用时的超时功能(timeout),用于等待文件描述符的状态变化。
- 这些函数可以设置超时时间,如果没有检测到IO,让调用线程挂起指定时间。
- 用法:
#include <stdio.h>
#include <poll.h>
#include <unistd.h>int main() {struct pollfd fds[1];fds[0].fd = STDIN_FILENO; // Monitor standard inputfds[0].events = POLLIN;int timeout = 5000; // 5-second timeoutprintf("Waiting for input (5 seconds timeout)...\n");int ret = poll(fds, 1, timeout);if (ret == -1) {perror("poll failed");} else if (ret == 0) {printf("Timeout occurred!\n");} else if (fds[0].revents & POLLIN) {char buffer[100];read(STDIN_FILENO, buffer, sizeof(buffer));printf("Input received: %s", buffer);}return 0;
}
- 特点:
- 高效且支持事件驱动(如结合
epoll
)。 - 适用于文件描述符管理的场景。
- 高效且支持事件驱动(如结合
- 依赖的内核机制
- 高分辨率定时器启用的情况下,使用hrtimer;否则使用tick-based定时器,取决于内核配置和硬件支持。
- 内核会将调用线程加入等待队列,并在超时到期或事件发生时唤醒线程。
6. 使用 **timerfd**
接口
- 功能
**timerfd**
: 通过文件描述符(FD)暴露定时器事件,适合与epoll
一起使用。- read()函数读取出来的,自上一次读取以来的所有未处理的超期次数。
- 用法:
#include <sys/timerfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>int main() {int tfd = timerfd_create(CLOCK_REALTIME, 0);struct itimerspec ts;ts.it_value.tv_sec = 2; // 初始触发时间ts.it_value.tv_nsec = 0;ts.it_interval.tv_sec = 2; // 周期触发时间ts.it_interval.tv_nsec = 0;timerfd_settime(tfd, 0, &ts, NULL);uint64_t expirations;while (1) {read(tfd, &expirations, sizeof(expirations)); // 阻塞等待printf("Timer expired %lu times\n", expirations); // expirations = 1}close(tfd);return 0;
}
- 特点:
- 高效且支持事件驱动(如结合
epoll
)。 - 适用于大量定时器、文件描述符管理的场景。
- 高效且支持事件驱动(如结合
- 依赖的内核机制
timerfd
基于内核的 hrtimer 实现。- 内核将定时器事件封装为文件描述符事件,通过 VFS 层传递给用户空间。
对比各种方法
方法 | 多定时器支持 | 周期性定时器 | 精度 | 内核机制 | 易用性 | 推荐用途 |
---|---|---|---|---|---|---|
alarm() | 否 | 否 | 秒级 | 低分辨率定时器 | 简单 | 单一的短时任务 |
setitimer() | 否 | 是 | 微秒级 | 低分辨率定时器 | 较简单 | 简单的周期性任务 |
POSIX 定时器 | 是 | 是 | 纳秒级 | 高分辨率定时器 | 较复杂 | 多任务,灵活的通知方式 |
sleep() / nanosleep() | 否 | 否 | 秒级/纳秒 | 低/高分辨率定时器 | 简单 | 简单的延时任务 |
poll() /select() /epoll() | 是 | 是 | 毫秒级 | 高分辨率定时器 | 较复杂 | 高效的文件描述符管理场景 |
timerfd | 是 | 是 | 纳秒级 | 高分辨率定时器 | 较复杂 | 高效的文件描述符管理场景 |
根据需求选择合适的定时器接口,可以满足不同的用户空间定时任务需求。
timerfd+IO多路复用组合使用
※※※ 推荐的方法,是**timerfd**
+**IO多路复用**
组合使用的方法。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <mqueue.h>
#include <sched.h>#define MAX_EVENTS 10
#define WORKER_COUNT 2
#define QUEUE_NAME "/task_queue"// 任务结构
typedef struct {int task_id;
} Task;// 线程信息
typedef struct {mqd_t mqd;pthread_t thread;int id;
} WorkerThread;WorkerThread workers[WORKER_COUNT];// 任务回调
void task_callback(int task_id) {printf("Worker processing task %d\n", task_id);sleep(1); // 模拟任务执行
}// 设定线程优先级
void set_thread_priority(pthread_t thread, int priority) {struct sched_param param;param.sched_priority = priority;pthread_setschedparam(thread, SCHED_FIFO, ¶m);
}// 工作线程函数
void *worker_thread(void *arg) {WorkerThread *worker = (WorkerThread *)arg;Task task;while (1) {if (mq_receive(worker->mqd, (char *)&task, sizeof(Task), NULL) > 0) {task_callback(task.task_id);}}
}// 选择负载最轻的工作线程
WorkerThread *get_least_busy_worker() {struct mq_attr attr;WorkerThread *best_worker = &workers[0];for (int i = 1; i < WORKER_COUNT; i++) {mq_getattr(workers[i].mqd, &attr);struct mq_attr best_attr;mq_getattr(best_worker->mqd, &best_attr);if (attr.mq_curmsgs < best_attr.mq_curmsgs) {best_worker = &workers[i];}}return best_worker;
}// 定时器检测线程
void *timer_thread(void *arg) {set_thread_priority(pthread_self(), 90);int epfd = epoll_create1(0);int timerfd = timerfd_create(CLOCK_REALTIME, 0);struct itimerspec timer_value = {.it_value = {2, 0}, .it_interval = {3, 0} };timerfd_settime(timerfd, 0, &timer_value, NULL);struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = timerfd;epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev);while (1) {struct epoll_event events[MAX_EVENTS];int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].data.fd == timerfd) {uint64_t exp;read(timerfd, &exp, sizeof(uint64_t));printf("Timer expired! Dispatching task...\n");static int task_id = 0;Task task = {task_id++};WorkerThread *worker = get_least_busy_worker();mq_send(worker->mqd, (char *)&task, sizeof(Task), 0);}}}
}// 主程序
int main() {struct mq_attr attr = {.mq_flags = 0,.mq_maxmsg = 10,.mq_msgsize = sizeof(Task),.mq_curmsgs = 0};// 初始化工作线程for (int i = 0; i < WORKER_COUNT; i++) {workers[i].id = i;workers[i].mqd = mq_open(QUEUE_NAME, O_CREAT | O_RDWR | O_NONBLOCK, 0644, &attr);pthread_create(&workers[i].thread, NULL, worker_thread, &workers[i]);set_thread_priority(workers[i].thread, 80);}// 启动定时器检测线程pthread_t timer_tid;pthread_create(&timer_tid, NULL, timer_thread, NULL);pthread_join(timer_tid, NULL);for (int i = 0; i < WORKER_COUNT; i++) {pthread_join(workers[i].thread, NULL);mq_close(workers[i].mqd);}mq_unlink(QUEUE_NAME);return 0;
}
还有一些定时器数据结构设计方法,参考之前另一篇定时器文章:《定时器实现》。
相关文章:

内核定时器3-用户空间定时器
用户空间定时器与内核定时器的关系 虽然用户空间定时器和内核定时器在实现上各自独立,但用户空间定时器通常依赖于内核定时器提供的基础设施。以下是具体关系: 依赖性 用户空间定时器的实现基于内核定时器。 例如,POSIX 定时器使用内核的 h…...

C++ 字面量深度解析:从基础到实战进阶
在 C 开发中,字面量(Literal)不仅是基础语法的一部分,更是提升代码可读性、安全性和性能的关键工具。本文将深入探讨 C 字面量的高级特性、最新标准支持(C11/14/17/20)以及实际开发中的应用技巧,…...

论文paper(更新...)
目录 是否rebuttal?rebuttal 技巧 是否rebuttal? 如果不rebuttal 肯定会拒稿,编辑也会给审稿人发信息,如下: Comment: Thanks for your efforts in reviewing this paper. The authors have neither submitted a rebu…...

变形金刚多元宇宙
1 涉及的公司 1.1 孩之宝HasBro 孩之宝与Takara签订协议后,孩之宝开始使用Takara的专利进行研发。 1.2 日本特佳丽Takara公司 特佳丽Takara Diaclone可变形恐龙的机器人玩具 Microman可变形汽车的机器人玩具 1.3 日本东映TOEI ANIMTION 1.4 美国漫威 为了推广玩具…...

HTTP协议的无状态和无连接
无连接 ①无连接的含义 这里所说的无连接并不是指不连接,客户与服务器之间的HTTP连接是一种一次性连接,它限制每次连接只处理一个请求,当服务器返回本次请求的应答后便立即关闭连接,下次请求再重新建立连接。这种一次性连接主要考…...

ASP.NET代码审计 SQL注入篇(简单记录)
sql注入,全局搜索 Request QueryString ToString() select select * aspx是设计页面,而aspx.cs是类页面,也就是说设计页面用到的类信息在这个页面里面,其实就是把设计和实现分离开来。 源码 using System; using System.Collect…...

毫秒级响应的VoIP中的系统组合推荐
在高并发、低延迟、毫秒级响应的 VoIP 场景中,选择合适的操作系统组合至关重要。以下是针对 Ubuntu linux-lowlatency、CentOS Stream kernel-rt 和 Debian 自定义 PREEMPT_RT 的详细对比及推荐: 1. 系统组合对比 特性Ubuntu linux-lowlatencyCentO…...

w186格障碍诊断系统spring boot设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...

shell -c
个人博客地址:shell -c | 一张假钞的真实世界 shell -c {string}:表示命令从-c后的字符串读取。在需要使用管道或者重定向需要sudo时很有用,如下: $ sudo find ../*/exportFiles -mtime 15 -name "*" | xargs -I {} r…...

(笔记+作业)书生大模型实战营春节卷王班---L1G3000 浦语提示词工程实践
学员闯关手册:https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 课程视频:https://www.bilibili.com/video/BV13U1VYmEUr/ 课程文档:https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 关卡作业:htt…...

文献学习笔记:中风醒脑液(FYTF-919)临床试验解读:有效还是无效?
【中风醒脑液(FYTF-919)临床试验解读:有效还是无效?】 在发表于 The Lancet (2024 年 11 月 30 日,第 404 卷)的临床研究《Traditional Chinese medicine FYTF-919 (Zhongfeng Xingnao oral pr…...

Chapter2 Amplifiers, Source followers Cascodes
Chapter2 Amplifiers, Source followers & Cascodes MOS单管根据输入输出, 可分为CS放大器, source follower和cascode 三种结构. Single-transistor amplifiers 这一章学习模拟电路基本单元-单管放大器 单管运放由Common-Source加上DC电流源组成. Avgm*Rds, gm和rds和…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(绘图设备封装)
目录 图像层的底层抽象——绘图设备抽象 如何抽象一个绘图设备? 桥接绘图设备,特化为OLED设备 题外话:设备的属性,与设计一个相似函数化简的通用办法 使用函数指针来操作设备 总结一下 图像层的底层抽象——绘图设备抽象 在…...

Android学习19 -- 手搓App
1 前言 之前工作中,很多时候要搞一个简单的app去验证底层功能,Android studio又过于重型,之前用gradle,被版本匹配和下载外网包折腾的堪称噩梦。所以搞app都只有找应用的同事帮忙。一直想知道一些简单的app怎么能手搓一下&#x…...

pytorch基于GloVe实现的词嵌入
PyTorch 实现 GloVe(Global Vectors for Word Representation) 的完整代码,使用 中文语料 进行训练,包括 共现矩阵构建、模型定义、训练和测试。 1. GloVe 介绍 基于词的共现信息(不像 Word2Vec 使用滑动窗口预测&…...

SpringCloud篇 微服务架构
1. 工程架构介绍 1.1 两种工程架构模型的特征 1.1.1 单体架构 上面这张图展示了单体架构(Monolithic Architecture)的基本组成和工作原理。单体架构是一种传统的软件架构模式,其中所有的功能都被打包在一个单一的、紧密耦合的应用程序中。 …...

背包问题和单调栈
背包问题(动态规划) 动态五步曲 dp数组及下标索引的含义递推公式dp数组如何初始化遍历顺序打印dp数组 01背包:n种物品,有一个,二维数组遍历顺序可以颠倒,(滚动数组)一维数组遍历顺序不可颠倒…...

Java | CompletableFuture详解
关注:CodingTechWork CompletableFuture 概述 介绍 CompletableFuture是 Java 8 引入的一个非常强大的类,属于 java.util.concurrent 包。它是用于异步编程的一个工具,可以帮助我们更方便地处理并发任务。与传统的线程池或 Future 对比&…...

【背包问题】二维费用的背包问题
目录 二维费用的背包问题详解 总结: 空间优化: 1. 状态定义 2. 状态转移方程 3. 初始化 4. 遍历顺序 5. 时间复杂度 例题 1,一和零 2,盈利计划 二维费用的背包问题详解 前面讲到的01背包中,对物品的限定条件…...

Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一…...

实验六 项目二 简易信号发生器的设计与实现 (HEU)
声明:代码部分使用了AI工具 实验六 综合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 实验项目 要求利用硬件描述语言Verilog(或VHDL)、图形描述方式、IP核,结合数字系统设计方法,在Quartus开发环境下ÿ…...

如何用微信小程序写春联
生活没有模板,只需心灯一盏。 如果笑能让你释然,那就开怀一笑;如果哭能让你减压,那就让泪水流下来。如果沉默是金,那就不用解释;如果放下能更好地前行,就别再扛着。 一、引入 Vant UI 1、通过 npm 安装 npm i @vant/weapp -S --production 2、修改 app.json …...

LabVIEW无人机航线控制系统
介绍了一种无人机航线控制系统,该系统利用LabVIEW软件与MPU6050九轴传感器相结合,实现无人机飞行高度、速度、俯仰角和滚动角的实时监控。系统通过虚拟仪器技术,有效实现了数据的采集、处理及回放,极大提高了无人机航线的控制精度…...

C++哈希表深度解析:从原理到实现,全面掌握高效键值对存储
目录 一、核心组件与原理 1. 哈希函数(Hash Function) 2. 冲突解决(Collision Resolution) 3. 负载因子(Load Factor)与扩容 二、C实现:std::unordered_map 1. 模板参数 2. 关键操作与复…...

Vue.js组件开发-实现字母向上浮动
使用Vue实现字母向上浮动的效果 实现步骤 创建Vue项目:使用Vue CLI来创建一个新的Vue项目。定义组件结构:在组件的模板中,定义包含字母的元素。添加样式:使用CSS动画来实现字母向上浮动的效果。绑定动画类:在Vue组件…...

自研有限元软件与ANSYS精度对比-Bar2D2Node二维杆单元模型-四连杆实例
目录 1、四连杆工程实例以及手算求解 2、四连杆的自研有限元软件求解 2.1、选择单元类型 2.2、导入四连杆工程 2.3、节点坐标定义 2.4、单元连接关系、材料定义 2.5、约束定义 2.6、外载定义 2.7、矩阵求解 2.8、变形云图展示 2.9、节点位移 2.10、单元应力 2.11、…...

04树 + 堆 + 优先队列 + 图(D1_树(D11_伸展树))
目录 一、基本介绍 二、伸展操作 1. 左右情况的伸展 2. 左左情况的伸展 3. 右左情况的伸展 4. 右右情况的伸展 三、其它操作 1. 插入 2. 删除 四、代码实现 一、基本介绍 伸展树是一种二叉搜索树,伸展树也是一种平衡树,不过伸展树并不像AVL树那…...

c语言练习题【数据类型、递归、双向链表快速排序】
练习1:数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*(指向 int 类型的指针) …...

SliverAppBar的功能和用法
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...

五、定时器实现呼吸灯
5.1 定时器与计数器简介 定时器是一种通过对内部时钟脉冲计数来测量时间间隔的模块。它的核心是一个递增或递减的寄存器(计数器值)。如果系统时钟为 1 MHz,定时器每 1 μs 计数一次。 计数器是一种对外部事件(如脉冲信号ÿ…...