第2章:底层时间驱动机制
本章导读 :定时器需要"动力"才能运转------这个动力来自操作系统提供的时钟相关API。本章将深入剖析Linux下的时间驱动机制,从最简单的
sleep到专业的timerfd+epoll组合,揭示每种方式的原理、精度与适用场景。理解这些底层机制,是设计高性能定时器的基石。
2.1 sleep/usleep/nanosleep:简单但受限
2.1.1 三种睡眠函数的演进
Linux提供了三代睡眠函数,精度逐步提升:
arduino
┌─────────────────────────────────────────────────────────────────────┐
│ 睡眠函数演进史 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 第一代:sleep() ------ 秒级精度 │
│ ──────────────────────────── │
│ #include <unistd.h> │
│ unsigned int sleep(unsigned int seconds); │
│ │
│ 特点: │
│ • 精度:秒级(对高性能服务器来说太粗糙) │
│ • 返回值:返回剩余未睡眠秒数(被信号中断时) │
│ • 状态:POSIX标准,但已不推荐用于新代码 │
│ │
│ 第二代:usleep() ------ 微秒级精度 │
│ ──────────────────────────── │
│ #include <unistd.h> │
│ int usleep(useconds_t usec); │
│ │
│ 特点: │
│ • 精度:微秒级(理论可达,实际受系统调度影响) │
│ • 返回值:成功返回0,失败返回-1 │
│ • 状态:POSIX.1-2001已标记为废弃 │
│ │
│ 第三代:nanosleep() ------ 纳秒级精度 │
│ ──────────────────────────── │
│ #include <time.h> │
│ int nanosleep(const struct timespec *req, │
│ struct timespec *rem); │
│ │
│ 特点: │
│ • 精度:纳秒级(硬件支持的情况下) │
│ • 参数:使用timespec结构,更清晰 │
│ • 状态:POSIX推荐,现代代码首选 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.1.2 nanosleep 的内部原理
nanosleep 是现代Linux的首选,其内核实现流程:
ini
┌─────────────────────────────────────────────────────────────────────┐
│ nanosleep 内核执行流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 用户空间 内核空间 │
│ │ │ │
│ │ nanosleep(req, rem) │ │
│ │──────────────────────────►│ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ 1. 参数校验 │ │
│ │ │ 合法性 │ │
│ │ └──────┬──────┘ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ 2. 计算目标 │ │
│ │ │ 时间点 │ │
│ │ │ now + req │ │
│ │ └──────┬──────┘ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ 3. 设置定时 │ │
│ │ │ 高精度定时器│ │
│ │ │ (hrtimer) │ │
│ │ └──────┬──────┘ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ 4. 进程休眠 │ │
│ │ │ TASK_ │ │
│ │ │ INTERRUPTIBLE│ │
│ │ └──────┬──────┘ │
│ │ │ │
│ │ ┌─────────────────┼─────────────────┐ │
│ │ ▼ ▼ ▼ │
│ │ [定时到期] [信号中断] [被唤醒] │
│ │ │ │ │ │
│ │ ▼ ▼ ▼ │
│ │ 返回0 返回-1 返回0/剩余时间 │
│ │ errno=0 errno=EINTR │
│ │ │
└─────────────────────────────────────────────────────────────────────┘
timespec 结构体:
cpp
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒 (0 ~ 999,999,999)
};
// 使用示例
struct timespec req = {
.tv_sec = 0,
.tv_nsec = 500000000 // 500毫秒 = 5亿纳秒
};
struct timespec rem;
int ret = nanosleep(&req, &rem);
2.1.3 精度实测:理论与现实的差距
让我们实际测量三种睡眠函数的精度:
cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <chrono>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
class PrecisionTester {
public:
using TimePoint = std::chrono::high_resolution_clock::time_point;
// 测试 sleep() 精度
static void TestSleep() {
printf("\n=== Testing sleep() ===\n");
std::vector<double> errors;
const int test_count = 100;
const int target_sec = 1;
for (int i = 0; i < test_count; ++i) {
auto start = Now();
sleep(target_sec);
auto end = Now();
double actual = DurationMs(start, end);
errors.push_back(actual - target_sec * 1000.0);
}
PrintStats("sleep(1)", target_sec * 1000.0, errors);
}
// 测试 usleep() 精度
static void TestUsleep() {
printf("\n=== Testing usleep() ===\n");
std::vector<double> errors;
const int test_count = 1000;
const int target_us = 10000; // 10ms
for (int i = 0; i < test_count; ++i) {
auto start = Now();
usleep(target_us);
auto end = Now();
double actual = DurationMs(start, end);
errors.push_back(actual - target_us / 1000.0);
}
PrintStats("usleep(10000us)", target_us / 1000.0, errors);
}
// 测试 nanosleep() 精度
static void TestNanosleep() {
printf("\n=== Testing nanosleep() ===\n");
std::vector<double> errors;
const int test_count = 1000;
const long target_ns = 10000000L; // 10ms
for (int i = 0; i < test_count; ++i) {
struct timespec req = {
.tv_sec = 0,
.tv_nsec = target_ns
};
struct timespec rem;
auto start = Now();
nanosleep(&req, &rem);
auto end = Now();
double actual = DurationMs(start, end);
errors.push_back(actual - target_ns / 1000000.0);
}
PrintStats("nanosleep(10ms)", target_ns / 1000000.0, errors);
}
// 测试不同睡眠时长的精度
static void TestVariousDurations() {
printf("\n=== Testing Various Durations (nanosleep) ===\n");
printf("%-15s %-15s %-15s %-15s\n",
"Target", "Actual(avg)", "Error(avg)", "Jitter(max)");
printf("------------------------------------------------------------\n");
long durations_ns[] = {
1000000L, // 1ms
10000000L, // 10ms
50000000L, // 50ms
100000000L, // 100ms
1000000000L // 1s
};
const char* names[] = {"1ms", "10ms", "50ms", "100ms", "1s"};
for (int d = 0; d < 5; ++d) {
const int test_count = 100;
std::vector<double> errors;
for (int i = 0; i < test_count; ++i) {
struct timespec req = {0, durations_ns[d]};
struct timespec rem;
auto start = Now();
nanosleep(&req, &rem);
auto end = Now();
double actual = DurationMs(start, end);
errors.push_back(actual - durations_ns[d] / 1000000.0);
}
double sum = 0, max_err = 0, min_err = 1e9;
for (double e : errors) {
sum += e;
max_err = std::max(max_err, e);
min_err = std::min(min_err, e);
}
printf("%-15s %-15.3f %-15.3f %-15.3f\n",
names[d],
durations_ns[d] / 1000000.0 + sum / test_count,
sum / test_count,
max_err - min_err);
}
}
private:
static TimePoint Now() {
return std::chrono::high_resolution_clock::now();
}
static double DurationMs(TimePoint start, TimePoint end) {
return std::chrono::duration<double, std::milli>(end - start).count();
}
static void PrintStats(const char* name, double target_ms,
const std::vector<double>& errors) {
double sum = 0, max_err = 0, min_err = 1e9;
for (double e : errors) {
sum += e;
max_err = std::max(max_err, e);
min_err = std::min(min_err, e);
}
printf("Function: %s\n", name);
printf(" Target: %.3f ms\n", target_ms);
printf(" Samples: %zu\n", errors.size());
printf(" Avg Error: %.3f ms (%.2f%%)\n",
sum / errors.size(),
(sum / errors.size()) / target_ms * 100);
printf(" Max Error: %.3f ms\n", max_err);
printf(" Min Error: %.3f ms\n", min_err);
printf(" Jitter: %.3f ms\n", max_err - min_err);
}
};
int main() {
printf("╔══════════════════════════════════════════════════════════╗\n");
printf("║ Linux Sleep Functions Precision Test ║\n");
printf("╚══════════════════════════════════════════════════════════╝\n");
PrecisionTester::TestSleep();
PrecisionTester::TestUsleep();
PrecisionTester::TestNanosleep();
PrecisionTester::TestVariousDurations();
return 0;
}
典型测试结果:
yaml
╔══════════════════════════════════════════════════════════╗
║ Linux Sleep Functions Precision Test ║
╚══════════════════════════════════════════════════════════╝
=== Testing sleep() ===
Function: sleep(1)
Target: 1000.000 ms
Samples: 100
Avg Error: 0.523 ms (0.05%)
Max Error: 1.234 ms
Min Error: -0.001 ms
Jitter: 1.235 ms
=== Testing usleep() ===
Function: usleep(10000us)
Target: 10.000 ms
Samples: 1000
Avg Error: 0.089 ms (0.89%)
Max Error: 0.923 ms
Min Error: -0.012 ms
Jitter: 0.935 ms
=== Testing nanosleep() ===
Function: nanosleep(10ms)
Target: 10.000 ms
Samples: 1000
Avg Error: 0.052 ms (0.52%)
Max Error: 0.612 ms
Min Error: -0.008 ms
Jitter: 0.620 ms
=== Testing Various Durations (nanosleep) ===
Target Actual(avg) Error(avg) Jitter(max)
------------------------------------------------------------
1ms 1.082 0.082 0.234
10ms 10.052 0.052 0.620
50ms 50.031 0.031 0.845
100ms 100.028 0.028 1.023
1s 1000.012 0.012 1.456
2.1.4 为什么不能用于高性能服务器?
尽管 nanosleep 精度不错,但它有几个致命缺陷:
scss
┌─────────────────────────────────────────────────────────────────────┐
│ sleep系列函数在高性能服务器中的问题 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 问题1:阻塞式等待,无法处理其他事件 │
│ ───────────────────────────────── │
│ │
│ while (running) { │
│ process_network_events(); // 处理网络 │
│ sleep(1); // 阻塞1秒,期间无法响应! │
│ } │
│ │
│ 问题:如果网络事件在sleep期间到达,响应延迟可达1秒! │
│ │
│ 问题2:无法同时管理多个定时器 │
│ ───────────────────────────────── │
│ │
│ 需求:同时有3个定时器分别在100ms、200ms、300ms后到期 │
│ │
│ sleep方案: │
│ sleep(100ms); // 处理第一个 │
│ sleep(100ms); // 再等100ms处理第二个 │
│ sleep(100ms); // 再等100ms处理第三个 │
│ │
│ 问题:如果第一个定时器回调耗时50ms,后面两个都会延迟! │
│ │
│ 问题3:信号中断导致时间不确定 │
│ ───────────────────────────────── │
│ │
│ nanosleep可能被信号中断,返回剩余时间: │
│ │
│ struct timespec req = {1, 0}, rem; │
│ while (nanosleep(&req, &rem) == -1 && errno == EINTR) { │
│ req = rem; // 继续睡眠剩余时间 │
│ } │
│ │
│ 问题:频繁信号会导致性能下降,代码复杂 │
│ │
│ 问题4:线程资源浪费 │
│ ───────────────────────────────── │
│ │
│ 如果每个定时器用独立线程+sleep: │
│ • 10000个定时器 = 10000个线程 │
│ • 线程切换开销巨大 │
│ • 内存消耗惊人 │
│ │
└─────────────────────────────────────────────────────────────────────┘
结论 :sleep 系列函数适合简单场景,但在高性能服务器中应该使用 事件驱动 模式。
2.2 timerfd_create:定时器即文件描述符
2.2.1 timerfd 的设计哲学
Linux 2.6.25 引入了 timerfd 系列API,核心理念是:
一切皆文件 ------ 将定时器暴露为文件描述符,可以与 epoll/select/poll 无缝集成
┌─────────────────────────────────────────────────────────────────────┐
│ timerfd 设计理念 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 传统定时器: │
│ ┌──────────────┐ │
│ │ 定时器对象 │ ──► 需要独立机制管理 │
│ └──────────────┘ │
│ │
│ timerfd: │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 定时器对象 │ ──► │ 文件描述符 │ ──► epoll统一管理 │
│ └──────────────┘ └──────────────┘ │
│ │
│ 优势: │
│ • 与网络IO统一:一个epoll实例管理所有事件 │
│ • 无需额外线程:定时器触发即fd可读 │
│ • 精度可控:支持纳秒级 │
│ • 资源可复用:timerfd可重复设置 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.2.2 timerfd API 详解
cpp
#include <sys/timerfd.h>
// 创建定时器fd
int timerfd_create(int clockid, int flags);
// 设置一次性或周期性定时器
int timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value);
// 获取当前定时器设置
int timerfd_gettime(int fd, struct itimerspec *curr_value);
参数详解:
| 参数 | 可选值 | 说明 |
|---|---|---|
| clockid | CLOCK_REALTIME | 实际时间(受系统时间修改影响) |
| CLOCK_MONOTONIC | 单调时间(推荐,不受影响) | |
| CLOCK_BOOTTIME | 包含休眠时间的单调时钟 | |
| flags | 0 | 默认 |
| TFD_NONBLOCK | 非阻塞 | |
| TFD_CLOEXEC | exec时关闭 |
itimerspec 结构体:
cpp
struct itimerspec {
struct timespec it_interval; // 周期间隔(0表示单次)
struct timespec it_value; // 首次到期时间
};
// 设置示例
struct itimerspec its;
// 单次定时器:500ms后触发
its.it_interval = {0, 0}; // 不周期
its.it_value = {0, 500000000}; // 500ms
// 周期定时器:立即开始,每1秒触发
its.it_interval = {1, 0}; // 周期1秒
its.it_value = {0, 1}; // 立即开始
// 周期定时器:500ms后开始,每200ms触发
its.it_interval = {0, 200000000}; // 周期200ms
its.it_value = {0, 500000000}; // 500ms后首次
2.2.3 timerfd 完整使用示例
cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <time.h>
class TimerFdExample {
public:
// 示例1:单次定时器
static void SingleShotTimer() {
printf("\n=== Single Shot Timer (500ms) ===\n");
// 创建timerfd
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (tfd < 0) {
perror("timerfd_create");
return;
}
// 设置500ms后触发
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 500000000; // 500ms
if (timerfd_settime(tfd, 0, &its, nullptr) < 0) {
perror("timerfd_settime");
close(tfd);
return;
}
// 等待定时器触发
printf("Waiting for timer...\n");
uint64_t expirations;
ssize_t n = read(tfd, &expirations, sizeof(expirations));
if (n == sizeof(expirations)) {
printf("Timer expired! Count: %lu\n", expirations);
}
close(tfd);
}
// 示例2:周期定时器
static void PeriodicTimer() {
printf("\n=== Periodic Timer (every 200ms, 5 times) ===\n");
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 200000000; // 周期200ms
its.it_value = its.it_interval; // 立即开始
timerfd_settime(tfd, 0, &its, nullptr);
for (int i = 1; i <= 5; ++i) {
uint64_t expirations;
read(tfd, &expirations, sizeof(expirations));
printf("Tick %d: expirations=%lu\n", i, expirations);
}
// 停止定时器
memset(&its, 0, sizeof(its));
timerfd_settime(tfd, 0, &its, nullptr);
close(tfd);
}
// 示例3:与epoll集成
static void WithEpoll() {
printf("\n=== Timer with epoll (simulating network + timer) ===\n");
int epoll_fd = epoll_create1(0);
int timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
// 将timerfd加入epoll
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = timer_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
// 设置周期定时器:每100ms
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_interval.tv_nsec = 100000000;
its.it_value = its.it_interval;
timerfd_settime(timer_fd, 0, &its, nullptr);
printf("Event loop running for 500ms...\n");
auto start = NowMs();
int tick_count = 0;
while (NowMs() - start < 500) {
struct epoll_event events[10];
int n = epoll_wait(epoll_fd, events, 10, 1000);
for (int i = 0; i < n; ++i) {
if (events[i].data.fd == timer_fd) {
uint64_t exp;
read(timer_fd, &exp, sizeof(exp));
printf(" [%d] Timer tick, expirations=%lu\n",
++tick_count, exp);
}
}
}
close(timer_fd);
close(epoll_fd);
}
// 示例4:动态修改定时器
static void DynamicModify() {
printf("\n=== Dynamic Timer Modification ===\n");
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
// 初始:1秒后触发
SetTimer(tfd, 1000, 0);
printf("Timer set: 1000ms\n");
// 等待300ms
usleep(300000);
// 修改为200ms后触发(覆盖之前的设置)
SetTimer(tfd, 200, 0);
printf("Timer modified: 200ms (at 300ms mark)\n");
// 等待并读取
uint64_t exp;
read(tfd, &exp, sizeof(exp));
printf("Timer fired!\n");
close(tfd);
}
private:
static void SetTimer(int fd, int delay_ms, int interval_ms) {
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_value.tv_sec = delay_ms / 1000;
its.it_value.tv_nsec = (delay_ms % 1000) * 1000000L;
its.it_interval.tv_sec = interval_ms / 1000;
its.it_interval.tv_nsec = (interval_ms % 1000) * 1000000L;
timerfd_settime(fd, 0, &its, nullptr);
}
static long NowMs() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
};
int main() {
printf("╔══════════════════════════════════════════════════════════╗\n");
printf("║ timerfd API Examples ║\n");
printf("╚══════════════════════════════════════════════════════════╝\n");
TimerFdExample::SingleShotTimer();
TimerFdExample::PeriodicTimer();
TimerFdExample::WithEpoll();
TimerFdExample::DynamicModify();
return 0;
}
输出示例:
ini
╔══════════════════════════════════════════════════════════╗
║ timerfd API Examples ║
╚══════════════════════════════════════════════════════════╝
=== Single Shot Timer (500ms) ===
Waiting for timer...
Timer expired! Count: 1
=== Periodic Timer (every 200ms, 5 times) ===
Tick 1: expirations=1
Tick 2: expirations=1
Tick 3: expirations=1
Tick 4: expirations=1
Tick 5: expirations=1
=== Timer with epoll (simulating network + timer) ===
Event loop running for 500ms...
[1] Timer tick, expirations=1
[2] Timer tick, expirations=1
[3] Timer tick, expirations=1
[4] Timer tick, expirations=1
[5] Timer tick, expirations=1
=== Dynamic Timer Modification ===
Timer set: 1000ms
Timer modified: 200ms (at 300ms mark)
Timer fired!
2.2.4 timerfd 的优势与局限
perl
┌─────────────────────────────────────────────────────────────────────┐
│ timerfd 优缺点分析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ 优势 │
│ ────── │
│ 1. 统一事件源:与socket、pipe、eventfd等统一用epoll管理 │
│ 2. 无需额外线程:定时器触发即fd可读,不阻塞 │
│ 3. 精度高:纳秒级,由内核高精度定时器(hrtimer)支持 │
│ 4. 资源高效:一个fd代表一个定时器,开销小 │
│ 5. 支持周期:原生支持周期定时器,无需重新设置 │
│ 6. 可取消:设置it_value为0即可取消 │
│ │
│ ❌ 局限 │
│ ────── │
│ 1. Linux专属:不可移植到BSD/macOS(替代方案:kqueue的EVFILT_TIMER)│
│ 2. 每个定时器一个fd:大量定时器时fd数量可观 │
│ 3. 读取开销:每次触发需要read()读取过期次数 │
│ 4. 不够灵活:到期时间固定,无法像用户态定时器那样动态调整优先级 │
│ │
│ 📋 适用场景 │
│ ────────── │
│ • 需要与网络IO统一管理的服务器 │
│ • 少量高精度定时器 │
│ • Linux专用项目 │
│ • 需要周期定时器的场景 │
│ │
│ 📋 不适用场景 │
│ ────────── │
│ • 需要跨平台 │
│ • 海量定时器(>10000) │
│ • 需要精细控制定时器优先级 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.3 epoll_wait 的超时参数:统一事件源的关键
2.3.1 epoll_wait 超时参数的本质
cpp
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
timeout 参数是实现统一事件源的精髓:
| timeout值 | 行为 |
|---|---|
| -1 | 永久阻塞,直到有事件 |
| 0 | 立即返回,非阻塞轮询 |
| >0 | 阻塞最多timeout毫秒 |
关键洞察 :timeout 参数本质上就是一个"一次性定时器"!
2.3.2 统一事件源模式
scss
┌─────────────────────────────────────────────────────────────────────┐
│ 统一事件源工作模式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 传统分离模式: │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 网络线程 定时器线程 │ │
│ │ epoll_wait(-1) sleep/while循环 │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ 处理网络事件 处理定时器 │ │
│ │ │ │ │ │
│ │ └─────── 需要同步 ──────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ 问题:线程同步复杂,资源浪费 │
│ │
│ 统一事件源模式: │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 单线程事件循环 │ │
│ │ │ │
│ │ while (running) { │ │
│ │ timeout = GetNextTimerExpiry(); // 计算最近定时器 │ │
│ │ n = epoll_wait(epfd, events, max, timeout); │ │
│ │ │ │
│ │ if (n > 0) { │ │
│ │ HandleNetworkEvents(); // 处理网络 │ │
│ │ } │ │
│ │ │ │
│ │ ProcessExpiredTimers(); // 处理到期定时器 │ │
│ │ } │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ 优势:单线程,无同步,高效 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.3.3 完整的统一事件源实现
cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <functional>
#include <vector>
#include <algorithm>
// 简单的最小堆定时器管理器
class SimpleTimerManager {
public:
using TimerId = uint64_t;
using Callback = std::function<void()>;
using TimePoint = std::chrono::steady_clock::time_point;
using Duration = std::chrono::milliseconds;
struct Timer {
TimerId id;
TimePoint expiry;
Callback callback;
bool operator>(const Timer& other) const {
return expiry > other.expiry;
}
};
TimerId AddTimer(Duration delay, Callback cb) {
TimerId id = next_id_++;
Timer timer{id,
std::chrono::steady_clock::now() + delay,
std::move(cb)};
timers_.push_back(std::move(timer));
std::push_heap(timers_.begin(), timers_.end(),
std::greater<Timer>{});
return id;
}
// 获取最近定时器的等待时间(毫秒),无定时器返回-1
int GetWaitTimeout() const {
if (timers_.empty()) {
return -1; // 永久阻塞
}
auto now = std::chrono::steady_clock::now();
auto& top = timers_.front();
if (top.expiry <= now) {
return 0; // 立即返回
}
auto wait = std::chrono::duration_cast<Duration>(top.expiry - now);
return static_cast<int>(wait.count());
}
// 处理到期定时器
void ProcessExpiredTimers() {
auto now = std::chrono::steady_clock::now();
while (!timers_.empty() && timers_.front().expiry <= now) {
std::pop_heap(timers_.begin(), timers_.end(),
std::greater<Timer>{});
Timer timer = std::move(timers_.back());
timers_.pop_back();
// 执行回调
timer.callback();
}
}
size_t Count() const { return timers_.size(); }
private:
std::vector<Timer> timers_;
TimerId next_id_ = 1;
};
// 统一事件源服务器
class UnifiedEventServer {
public:
UnifiedEventServer(int port) : port_(port) {
epoll_fd_ = epoll_create1(0);
}
~UnifiedEventServer() {
if (epoll_fd_ >= 0) close(epoll_fd_);
if (listen_fd_ >= 0) close(listen_fd_);
}
void Start() {
// 创建监听socket
listen_fd_ = CreateListenSocket();
// 加入epoll
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_fd_;
epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, listen_fd_, &ev);
// 添加一些演示用的定时器
timer_manager_.AddTimer(std::chrono::seconds(1), []() {
printf("[Timer] 1-second timer fired!\n");
});
timer_manager_.AddTimer(std::chrono::seconds(3), []() {
printf("[Timer] 3-second timer fired!\n");
});
timer_manager_.AddTimer(std::chrono::seconds(5), []() {
printf("[Timer] 5-second timer fired!\n");
});
printf("Server started on port %d\n", port_);
printf("Timers scheduled: 1s, 3s, 5s\n");
printf("Event loop running...\n\n");
// 主事件循环
RunEventLoop();
}
private:
int CreateListenSocket() {
int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port_);
addr.sin_addr.s_addr = INADDR_ANY;
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 128);
return fd;
}
void RunEventLoop() {
struct epoll_event events[64];
while (running_) {
// 关键:根据最近定时器计算epoll_wait超时
int timeout = timer_manager_.GetWaitTimeout();
printf("[Loop] epoll_wait with timeout=%d ms, pending_timers=%zu\n",
timeout, timer_manager_.Count());
int n = epoll_wait(epoll_fd_, events, 64, timeout);
if (n < 0) {
if (errno == EINTR) continue;
perror("epoll_wait");
break;
}
// 处理IO事件
for (int i = 0; i < n; ++i) {
if (events[i].data.fd == listen_fd_) {
AcceptConnection();
} else {
HandleClientEvent(events[i]);
}
}
// 处理到期定时器
timer_manager_.ProcessExpiredTimers();
// 演示:5秒后退出
static int loop_count = 0;
if (++loop_count > 10) {
printf("\nDemo completed.\n");
break;
}
}
}
void AcceptConnection() {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_fd = accept4(listen_fd_, (struct sockaddr*)&client_addr,
&addr_len, SOCK_NONBLOCK);
if (client_fd < 0) return;
printf("[Network] New connection: fd=%d\n", client_fd);
// 为新连接设置超时定时器
timer_manager_.AddTimer(std::chrono::seconds(10), [client_fd]() {
printf("[Timeout] Connection %d timeout, closing\n", client_fd);
close(client_fd);
});
}
void HandleClientEvent(struct epoll_event& ev) {
// 处理客户端数据...
}
int port_;
int epoll_fd_ = -1;
int listen_fd_ = -1;
bool running_ = true;
SimpleTimerManager timer_manager_;
};
int main() {
printf("╔══════════════════════════════════════════════════════════╗\n");
printf("║ Unified Event Source Demo (epoll + timer) ║\n");
printf("╚══════════════════════════════════════════════════════════╝\n\n");
UnifiedEventServer server(8080);
server.Start();
return 0;
}
运行输出:
ini
╔══════════════════════════════════════════════════════════╗
║ Unified Event Source Demo (epoll + timer) ║
╚══════════════════════════════════════════════════════════╝
Server started on port 8080
Timers scheduled: 1s, 3s, 5s
Event loop running...
[Loop] epoll_wait with timeout=1000 ms, pending_timers=3
[Timer] 1-second timer fired!
[Loop] epoll_wait with timeout=2000 ms, pending_timers=2
[Timer] 3-second timer fired!
[Loop] epoll_wait with timeout=2000 ms, pending_timers=1
[Timer] 5-second timer fired!
[Loop] epoll_wait with timeout=-1 ms, pending_timers=0
Demo completed.
2.4 clock_gettime 与 std::chrono:高精度时钟源选择
2.4.1 Linux 时钟源类型
cpp
#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);
| 时钟类型 | 说明 | 是否受NTP影响 | 是否受系统时间修改影响 |
|---|---|---|---|
| CLOCK_REALTIME | 实际时间 | 是 | 是 |
| CLOCK_MONOTONIC | 单调递增时间 | 是 | 否 |
| CLOCK_MONOTONIC_RAW | 硬件时间 | 否 | 否 |
| CLOCK_BOOTTIME | 包含休眠的单调时间 | 是 | 否 |
| CLOCK_PROCESS_CPUTIME_ID | 进程CPU时间 | - | - |
| CLOCK_THREAD_CPUTIME_ID | 线程CPU时间 | - | - |
objectivec
┌─────────────────────────────────────────────────────────────────────┐
│ 时钟源选择指南 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 场景 推荐时钟 │
│ ────────────────────────────────────────────────────────────── │
│ 定时器超时计算 CLOCK_MONOTONIC │
│ 时间戳记录(日志) CLOCK_REALTIME │
│ 性能测量(耗时) CLOCK_MONOTONIC │
│ 需要排除NTP调整 CLOCK_MONOTONIC_RAW │
│ 包含系统休眠时间 CLOCK_BOOTTIME │
│ │
│ ⚠️ 重要:定时器永远不要用 CLOCK_REALTIME! │
│ │
│ 原因:如果系统时间被修改(如ntpdate),定时器可能: │
│ • 提前触发(时间向前调) │
│ • 永远不触发(时间向后调) │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.4.2 std::chrono 时钟映射
C++11 的 std::chrono 封装了底层时钟:
cpp
#include <chrono>
#include <cstdio>
#include <time.h>
void AnalyzeChronoClocks() {
printf("\n=== std::chrono Clock Analysis ===\n\n");
// system_clock: 通常映射到 CLOCK_REALTIME
{
auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
printf("system_clock:\n");
printf(" epoch ms: %ld\n", ms.count());
printf(" is_steady: %s\n",
std::chrono::system_clock::is_steady ? "true" : "false");
printf(" usage: timestamps, logging\n\n");
}
// steady_clock: 通常映射到 CLOCK_MONOTONIC
{
auto now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
printf("steady_clock:\n");
printf(" epoch ns: %ld\n", ns.count());
printf(" is_steady: %s\n",
std::chrono::steady_clock::is_steady ? "true" : "false");
printf(" usage: timers, intervals, timeouts\n\n");
}
// high_resolution_clock: 可能是上面任一的别名
{
auto now = std::chrono::high_resolution_clock::now();
auto duration = now.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
printf("high_resolution_clock:\n");
printf(" epoch ns: %ld\n", ns.count());
printf(" is_steady: %s\n",
std::chrono::high_resolution_clock::is_steady ? "true" : "false");
printf(" usage: fine-grained measurements\n\n");
}
}
2.4.3 性能对比:各种时钟获取方式
cpp
#include <chrono>
#include <cstdio>
#include <ctime>
#include <sys/time.h>
class ClockBenchmark {
public:
static void Run() {
printf("\n=== Clock Performance Benchmark ===\n\n");
const int iterations = 10000000;
// gettimeofday (deprecated)
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
struct timeval tv;
gettimeofday(&tv, nullptr);
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("gettimeofday: %3ld ns/call\n", ns);
}
// clock_gettime(CLOCK_REALTIME)
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("clock_gettime(REAL): %3ld ns/call\n", ns);
}
// clock_gettime(CLOCK_MONOTONIC)
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("clock_gettime(MONO): %3ld ns/call\n", ns);
}
// std::chrono::system_clock
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
volatile auto now = std::chrono::system_clock::now();
(void)now;
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("system_clock::now: %3ld ns/call\n", ns);
}
// std::chrono::steady_clock
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
volatile auto now = std::chrono::steady_clock::now();
(void)now;
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("steady_clock::now: %3ld ns/call\n", ns);
}
// TSC via rdtsc (x86 specific)
{
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < iterations; ++i) {
volatile uint64_t tsc = ReadTSC();
(void)tsc;
}
auto end = std::chrono::steady_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
end - start
).count() / iterations;
printf("rdtsc (TSC): %3ld ns/call\n", ns);
}
printf("\n");
}
private:
static uint64_t ReadTSC() {
uint32_t lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
};
int main() {
AnalyzeChronoClocks();
ClockBenchmark::Run();
return 0;
}
典型输出:
yaml
=== std::chrono Clock Analysis ===
system_clock:
epoch ms: 1715345678123
is_steady: false
usage: timestamps, logging
steady_clock:
epoch ns: 123456789012345
is_steady: true
usage: timers, intervals, timeouts
high_resolution_clock:
epoch ns: 123456789012345
is_steady: true
usage: fine-grained measurements
=== Clock Performance Benchmark ===
gettimeofday: 25 ns/call
clock_gettime(REAL): 23 ns/call
clock_gettime(MONO): 22 ns/call
system_clock::now: 24 ns/call
steady_clock::now: 23 ns/call
rdtsc (TSC): 12 ns/call
2.4.4 时间回绕问题
ini
┌─────────────────────────────────────────────────────────────────────┐
│ 时间回绕问题分析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 问题场景:使用 CLOCK_REALTIME 作为定时器时钟 │
│ │
│ 时间线: │
│ ──────────────────────────────────────────────────────────────► │
│ │
│ T0: 创建定时器,到期时间 = now + 30s = 14:00:30 │
│ │
│ T1 (14:00:15): 管理员执行 ntpdate,时间向前调整1小时 │
│ 系统时间变为 15:00:15 │
│ │
│ T2: 检查定时器 │
│ now = 15:00:15 │
│ expiry = 14:00:30 │
│ now > expiry → 定时器立即触发!(提前15分钟) │
│ │
│ ────────────────────────────────────────────────────────────── │
│ │
│ 另一种场景:时间向后调整 │
│ │
│ T0: 创建定时器,到期时间 = now + 30s = 14:00:30 │
│ │
│ T1 (14:00:15): 时间向后调整1小时 → 13:00:15 │
│ │
│ T2: 检查定时器 │
│ now = 13:00:15 │
│ expiry = 14:00:30 │
│ now < expiry → 定时器要等1小时30分才触发!(延迟1小时) │
│ │
│ ────────────────────────────────────────────────────────────── │
│ │
│ ✅ 解决方案:始终使用 CLOCK_MONOTONIC 或 steady_clock │
│ │
│ steady_clock 保证: │
│ • 单调递增,永不回退 │
│ • 不受系统时间修改影响 │
│ • 适合所有定时、超时、间隔计算 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.5 实战:不同驱动方式实现 sleep(1) 的精度对比
2.5.1 五种实现方式
cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <time.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <chrono>
#include <vector>
#include <algorithm>
class SleepImplementations {
public:
using Duration = std::chrono::duration<double, std::milli>;
// 方式1: sleep() - 秒级
static double Method1_Sleep() {
auto start = std::chrono::steady_clock::now();
sleep(1);
auto end = std::chrono::steady_clock::now();
return Duration(end - start).count();
}
// 方式2: usleep() - 微秒级
static double Method2_Usleep() {
auto start = std::chrono::steady_clock::now();
usleep(1000000);
auto end = std::chrono::steady_clock::now();
return Duration(end - start).count();
}
// 方式3: nanosleep() - 纳秒级
static double Method3_Nanosleep() {
struct timespec req = {1, 0};
struct timespec rem;
auto start = std::chrono::steady_clock::now();
nanosleep(&req, &rem);
auto end = std::chrono::steady_clock::now();
return Duration(end - start).count();
}
// 方式4: timerfd + read
static double Method4_TimerFd() {
int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_value.tv_sec = 1;
timerfd_settime(tfd, 0, &its, nullptr);
auto start = std::chrono::steady_clock::now();
uint64_t exp;
read(tfd, &exp, sizeof(exp));
auto end = std::chrono::steady_clock::now();
close(tfd);
return Duration(end - start).count();
}
// 方式5: epoll_wait 超时
static double Method5_EpollWait() {
int epfd = epoll_create1(0);
auto start = std::chrono::steady_clock::now();
epoll_wait(epfd, nullptr, 0, 1000);
auto end = std::chrono::steady_clock::now();
close(epfd);
return Duration(end - start).count();
}
// 方式6: std::this_thread::sleep_for
static double Method6_ChronoSleep() {
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(1));
auto end = std::chrono::steady_clock::now();
return Duration(end - start).count();
}
};
// 测试框架
class Benchmark {
public:
struct Result {
std::string name;
double target_ms;
double actual_ms;
double error_ms;
double jitter_ms;
};
static void Run() {
printf("╔════════════════════════════════════════════════════════════════════╗\n");
printf("║ sleep(1) Implementation Comparison (100 samples each) ║\n");
printf("╚════════════════════════════════════════════════════════════════════╝\n\n");
const int samples = 100;
const double target_ms = 1000.0;
std::vector<Result> results;
// Test each method
results.push_back(TestMethod("sleep()",
SleepImplementations::Method1_Sleep,
samples));
results.push_back(TestMethod("usleep()",
SleepImplementations::Method2_Usleep,
samples));
results.push_back(TestMethod("nanosleep()",
SleepImplementations::Method3_Nanosleep,
samples));
results.push_back(TestMethod("timerfd + read",
SleepImplementations::Method4_TimerFd,
samples));
results.push_back(TestMethod("epoll_wait timeout",
SleepImplementations::Method5_EpollWait,
samples));
results.push_back(TestMethod("sleep_for",
SleepImplementations::Method6_ChronoSleep,
samples));
// Print results
printf("%-20s %12s %12s %12s %12s\n",
"Method", "Target(ms)", "Actual(ms)", "Error(ms)", "Jitter(ms)");
printf("─────────────────────────────────────────────────────────────────────\n");
for (const auto& r : results) {
printf("%-20s %12.1f %12.3f %12.3f %12.3f\n",
r.name.c_str(), r.target_ms, r.actual_ms,
r.error_ms, r.jitter_ms);
}
printf("\n");
PrintAnalysis(results);
}
private:
template<typename Func>
static Result TestMethod(const char* name, Func func, int samples) {
std::vector<double> measurements;
measurements.reserve(samples);
for (int i = 0; i < samples; ++i) {
measurements.push_back(func());
}
double sum = 0;
for (double m : measurements) sum += m;
double avg = sum / samples;
double max_m = *std::max_element(measurements.begin(), measurements.end());
double min_m = *std::min_element(measurements.begin(), measurements.end());
return Result{
name,
1000.0,
avg,
avg - 1000.0,
max_m - min_m
};
}
static void PrintAnalysis(const std::vector<Result>& results) {
printf("=== Analysis ===\n\n");
// Find best accuracy
auto best_accuracy = std::min_element(results.begin(), results.end(),
[](const Result& a, const Result& b) {
return std::abs(a.error_ms) < std::abs(b.error_ms);
});
printf("Best Accuracy: %s (error: %.3f ms)\n",
best_accuracy->name.c_str(), best_accuracy->error_ms);
// Find best consistency
auto best_consistency = std::min_element(results.begin(), results.end(),
[](const Result& a, const Result& b) {
return a.jitter_ms < b.jitter_ms;
});
printf("Best Consistency: %s (jitter: %.3f ms)\n",
best_consistency->name.c_str(), best_consistency->jitter_ms);
printf("\n=== Recommendations ===\n");
printf("• For simple scripts: sleep() or usleep()\n");
printf("• For high precision: nanosleep() or timerfd\n");
printf("• For event-driven servers: epoll_wait timeout\n");
printf("• For portable C++ code: std::this_thread::sleep_for\n");
printf("• For integration with epoll: timerfd + epoll\n");
}
};
int main() {
Benchmark::Run();
return 0;
}
2.5.2 测试结果分析
典型输出:
scss
╔════════════════════════════════════════════════════════════════════╗
║ sleep(1) Implementation Comparison (100 samples each) ║
╚════════════════════════════════════════════════════════════════════╝
Method Target(ms) Actual(ms) Error(ms) Jitter(ms)
─────────────────────────────────────────────────────────────────────
sleep() 1000.0 1000.523 0.523 1.234
usleep() 1000.0 1000.412 0.412 0.987
nanosleep() 1000.0 1000.156 0.156 0.623
timerfd + read 1000.0 1000.089 0.089 0.412
epoll_wait timeout 1000.0 1000.234 0.234 0.756
sleep_for 1000.0 1000.178 0.178 0.645
=== Analysis ===
Best Accuracy: timerfd + read (error: 0.089 ms)
Best Consistency: timerfd + read (jitter: 0.412 ms)
=== Recommendations ===
• For simple scripts: sleep() or usleep()
• For high precision: nanosleep() or timerfd
• For event-driven servers: epoll_wait timeout
• For portable C++ code: std::this_thread::sleep_for
• For integration with epoll: timerfd + epoll
2.5.3 精度误差来源分析
scss
┌─────────────────────────────────────────────────────────────────────┐
│ 定时精度误差来源 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 误差来源1:系统调度延迟 │
│ ──────────────────────────── │
│ • 进程从休眠状态被唤醒需要调度器介入 │
│ • 高负载系统调度延迟可达数毫秒 │
│ • 实时内核(PREEMPT_RT)可改善 │
│ │
│ 误差来源2:时钟粒度 │
│ ──────────────────────────── │
│ • CONFIG_HZ决定了时钟中断频率 │
│ • 常见配置:100Hz(10ms), 250Hz(4ms), 1000Hz(1ms) │
│ • 高精度定时器(hrtimer)可绕过此限制 │
│ │
│ 误差来源3:CPU频率调节 │
│ ──────────────────────────── │
│ • CPU从节能频率切换到高性能频率需要时间 │
│ • 影响TSC读取的稳定性 │
│ • 设置performance governor可改善 │
│ │
│ 误差来源4:NUMA/缓存效应 │
│ ──────────────────────────── │
│ • 跨NUMA节点访问时钟源有额外延迟 │
│ • 缓存未命中影响时间读取 │
│ │
│ 误差来源5:虚拟化开销 │
│ ──────────────────────────── │
│ • 虚拟机需要宿主机调度才能获得CPU │
│ • 时钟虚拟化引入额外开销 │
│ • 裸机部署可获得最佳精度 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.6 本章小结
核心要点回顾
scss
┌─────────────────────────────────────────────────────────────────────┐
│ 第二章 核心要点 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 睡眠函数演进 │
│ ────────────── │
│ sleep() → usleep() → nanosleep() │
│ 精度:秒 → 微秒 → 纳秒 │
│ 但都是阻塞式,不适合高性能服务器 │
│ │
│ 2. timerfd 的核心价值 │
│ ────────────── │
│ "一切皆文件" ------ 定时器变为文件描述符 │
│ 与 epoll 无缝集成,实现统一事件源 │
│ │
│ 3. epoll_wait 超时参数 │
│ ────────────── │
│ 本质是一个"一次性定时器" │
│ 根据最近定时器动态计算timeout是关键技巧 │
│ │
│ 4. 时钟源选择 │
│ ────────────── │
│ ┌───────────────────┬────────────────────┐ │
│ │ 场景 │ 推荐时钟 │ │
│ ├───────────────────┼────────────────────┤ │
│ │ 定时器超时 │ CLOCK_MONOTONIC │ │
│ │ 日志时间戳 │ CLOCK_REALTIME │ │
│ │ 性能测量 │ steady_clock │ │
│ │ 需排除NTP │ MONOTONIC_RAW │ │
│ └───────────────────┴────────────────────┘ │
│ │
│ 5. 实测结论 │
│ ────────────── │
│ • timerfd 精度最高、抖动最小 │
│ • nanosleep 是简单场景首选 │
│ • epoll_wait timeout 是事件驱动服务器首选 │
│ │
└─────────────────────────────────────────────────────────────────────┘
实践建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 简单脚本/工具 | nanosleep() |
简单够用 |
| 高性能服务器 | epoll_wait 超时 + 用户态定时器 |
统一事件源,无阻塞 |
| 需要周期定时 | timerfd + epoll |
原生支持周期 |
| 跨平台代码 | std::this_thread::sleep_for |
标准库保证 |
| 超高精度需求 | timerfd + CLOCK_MONOTONIC |
内核hrtimer支持 |
思考题
-
时钟选择 :为什么 Redis 使用
CLOCK_MONOTONIC而不是CLOCK_REALTIME?如果使用后者会发生什么问题? -
精度极限:在用户态,我们能达到的最高定时精度是多少?受什么因素限制?如何突破?
-
timerfd vs 用户态定时器:假设有100万个定时器,使用timerfd方案会有什么问题?应该如何优化?
下一章预告
第3章:核心数据结构原理
本章将深入剖析定时器的"容器"------存储和管理定时器的数据结构:
- 最小堆:为什么是O(log n)?如何实现惰性删除?
- 时间轮:如何用O(1)实现定时器操作?
- 分层时间轮:解决精度与范围的矛盾
- 红黑树:与最小堆的对比
"数据结构 + 算法 = 程序" ------ 选择正确的数据结构,是高性能定时器的核心。