[TOC]

include/linux/ktime.h

ktime_set 从秒/纳秒值设置ktime_t变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* ktime_set - 从秒/纳秒值设置ktime_t变量
* @secs:秒设置
* @nsecs:纳秒设置
*
* 返回:值的ktime_t表示形式。
*/
static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
{
if (unlikely(secs >= KTIME_SEC_MAX))
return KTIME_MAX;

return secs * NSEC_PER_SEC + (s64)nsecs;
}

/* Subtract two ktime_t variables. rem = lhs -rhs: */
#define ktime_sub(lhs, rhs) ((lhs) - (rhs))

/* Add two ktime_t variables. res = lhs + rhs: */
#define ktime_add(lhs, rhs) ((lhs) + (rhs))


/* 将 TimeSpec64 转换为 ktime_t 格式: */
static inline ktime_t timespec64_to_ktime(struct timespec64 ts)
{
return ktime_set(ts.tv_sec, ts.tv_nsec);
}

include/linux/time64.h

timespec64_sub sub = lhs - rhs,以标准化形式

1
2
3
4
5
6
7
8
9
10
11
/*
* sub = lhs - rhs,以标准化形式
*/
static inline struct timespec64 timespec64_sub(struct timespec64 lhs,
struct timespec64 rhs)
{
struct timespec64 ts_delta;
set_normalized_timespec64(&ts_delta, lhs.tv_sec - rhs.tv_sec,
lhs.tv_nsec - rhs.tv_nsec);
return ts_delta;
}

timespec64_valid Timespec64结构体 有效性验证

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
*如果 timespec64 为 norm,则返回 true,如果 denorm,则返回 false:
*/
static inline bool timespec64_valid(const struct timespec64 *ts)
{
/* Dates before 1970 are bogus */
if (ts->tv_sec < 0)
return false;
/* Can't have more nanoseconds then a second */
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
return false;
return true;
}

timespec64_valid_settod Timespec64结构体 有效性 和 溢出验证

1
2
3
4
5
6
7
8
9
static inline bool timespec64_valid_settod(const struct timespec64 *ts)
{
if (!timespec64_valid(ts))
return false;
/* 不允许导致溢出问题的值 vs. CLOCK_REALTIME */
if ((unsigned long long)ts->tv_sec >= TIME_SETTOD_SEC_MAX)
return false;
return true;
}

timespec64_compare Timespec64 比较

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* lhs < rhs: return <0
* lhs == rhs: return 0
* lhs > rhs: return >0
*/
static inline int timespec64_compare(const struct timespec64 *lhs, const struct timespec64 *rhs)
{
if (lhs->tv_sec < rhs->tv_sec)
return -1;
if (lhs->tv_sec > rhs->tv_sec)
return 1;
return lhs->tv_nsec - rhs->tv_nsec;
}

include/linux/timerqueue.h

timerqueue_getnext 返回具有最早到期时间的定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* timerqueue_getnext - 返回具有最早到期时间的定时器
*
* @head: 定时器队列的头部
*
* 返回指向具有最早到期时间的定时器节点的指针。
*/
static inline
struct timerqueue_node *timerqueue_getnext(struct timerqueue_head *head)
{
struct rb_node *leftmost = rb_first_cached(&head->rb_root);

return rb_entry_safe(leftmost, struct timerqueue_node, node);
}

kernel/sched/cputime.c

account_process_tick 统计一个tick的CPU时间

  • 它的核心作用是:将一个时钟节拍(tick)所代表的CPU时间(TICK_NSEC纳秒),根据当前上下文(用户态、内核态、或空闲态),精确地累加到目标任务p或系统的相应时间统计中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
* 核算一整个tick的CPU时间。
* @p: CPU时间被记到其头上的那个进程。
* @user_tick: 指示这个tick是用户时间还是系统时间。
*/
void account_process_tick(struct task_struct *p, int user_tick)
{
/* cputime: 存储一个tick代表的总纳秒数。*/
u64 cputime;
/* steal: 存储从虚拟机宿主机那里被“窃取”的CPU时间。*/
u64 steal;

/* 如果当前CPU开启了虚拟时间记账(vtime),则不使用基于tick的记账,直接返回。*/
// if (vtime_accounting_enabled_this_cpu())
// return;

/* 如果开启了精确中断时间记账(irqtime)... */
// if (irqtime_enabled()) {
/* ...则将记账工作委托给专门的irqtime处理函数并返回。*/
// irqtime_account_process_tick(p, user_tick, 1);
// return;
// }

/* 获取一个tick周期的标准纳秒数。*/
cputime = TICK_NSEC;
/*
* 计算并核算从上次记账以来,被hypervisor窃走的CPU时间。
* ULONG_MAX表示我们不关心具体的cycle数,只关心时间。
*/
// steal = steal_account_process_time(ULONG_MAX);

/* 如果被窃取的时间大于等于整个tick周期,说明本tick内没有有效时间,直接返回。*/
// if (steal >= cputime)
// return;

/* 从总时间中减去被窃取的时间,得到任务实际可用的CPU时间。*/
// cputime -= steal;

/* 如果这个tick是在用户态发生的...*/
if (user_tick)
/* ...则调用account_user_time,将cputime记为任务p的用户态时间。*/
account_user_time(p, cputime);
/* 否则,如果是在内核态发生的,并且当前任务p不是空闲线程(idle)...
* (irq_count() != HARDIRQ_OFFSET这个检查用于排除从idle循环中
* 发生的、但不在硬中断上下文中的tick)*/
else if ((p != this_rq()->idle) || (irq_count() != HARDIRQ_OFFSET))
/* ...则调用account_system_time,将cputime记为任务p的内核态时间。
* HARDIRQ_OFFSET用于区分是纯内核态还是硬中断上下文。*/
account_system_time(p, HARDIRQ_OFFSET, cputime);
else
/* 否则,如果是在内核态,并且任务是idle线程,则调用account_idle_time,
* 将cputime记为该CPU的空闲时间。*/
account_idle_time(cputime);
}

arch/arm/kernel/time.c

time_init

1
2
3
4
5
6
7
8
9
10
11
12
void __init time_init(void)
{
if (machine_desc->init_time) {
machine_desc->init_time();
} else {
#ifdef CONFIG_COMMON_CLK
of_clk_init(NULL);
#endif
timer_probe();
tick_setup_hrtimer_broadcast();
}
}

kernel/time/time.c

set_normalized_timespec64 设置并规范化 timespec64 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* set_normalized_timespec64 - 设置 TimeSpec 秒和 NSec 部分并归一化
*
* @ts:指向要设置的 timespec 变量的指针
* @sec:秒设置
* @nsec:纳秒设置
*
* 设置 timespec 变量的 seconds 和 náns 字段,并规范化为 timespec 存储格式
*
* 注意:tv_nsec部分始终在 0 <= tv_nsec < NSEC_PER_SEC 的范围内。对于负值,只有 tv_sec 字段为负数!
*/
void set_normalized_timespec64(struct timespec64 *ts, time64_t sec, s64 nsec)
{
/* 如果 nsec 的值大于或等于 NSEC_PER_SEC(每秒的纳秒数,通常为 10^9),则通过循环将多余的纳秒转换为秒 */
while (nsec >= NSEC_PER_SEC) {
/*
*以下 asm() 可防止编译器将此循环优化为模运算。
* 另请参见 include/linux/time.h 中的 __iter_div_u64_rem()
*/
/* 每次循环减少 NSEC_PER_SEC 纳秒,同时将对应的秒数加到 sec 中 */
asm("" : "+rm"(nsec));
nsec -= NSEC_PER_SEC;
++sec;
}
/* 如果 nsec 的值小于 0,则通过循环将负的纳秒值转换为秒 */
while (nsec < 0) {
asm("" : "+rm"(nsec));
nsec += NSEC_PER_SEC;
--sec;
}
ts->tv_sec = sec;
ts->tv_nsec = nsec;
}
EXPORT_SYMBOL(set_normalized_timespec64);

ns_to_timespec64 - 将纳秒转换为 timespec64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* ns_to_timespec64 - 将纳秒转换为 timespec64
* @nsec:要转换的纳秒值
*
* 返回:nsec 参数的 timespec64 表示形式。
*/
struct timespec64 ns_to_timespec64(s64 nsec)
{
struct timespec64 ts = { 0, 0 };
s32 rem;

if (likely(nsec > 0)) {
ts.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
ts.tv_nsec = rem;
} else if (nsec < 0) {
/*
* With negative times, tv_sec points to the earlier
* second, and tv_nsec counts the nanoseconds since
* then, so tv_nsec is always a positive number.
*/
ts.tv_sec = -div_u64_rem(-nsec - 1, NSEC_PER_SEC, &rem) - 1;
ts.tv_nsec = NSEC_PER_SEC - rem - 1;
}

return ts;
}
EXPORT_SYMBOL(ns_to_timespec64);

kernel/time/ntp.c

__ntp_clear

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void __ntp_clear(struct ntp_data *ntpdata)
{
/* Stop active adjtime() */
ntpdata->time_adjust = 0;
ntpdata->time_status |= STA_UNSYNC;
ntpdata->time_maxerror = NTP_PHASE_LIMIT;
ntpdata->time_esterror = NTP_PHASE_LIMIT;

ntp_update_frequency(ntpdata);

ntpdata->tick_length = ntpdata->tick_length_base;
ntpdata->time_offset = 0;

ntpdata->ntp_next_leap_sec = TIME64_MAX;
/* Clear PPS state variables */
pps_clear(ntpdata);
}

ntp_clear

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static struct ntp_data tk_ntp_data = {
.tick_usec = USER_TICK_USEC,
.time_state = TIME_OK,
.time_status = STA_UNSYNC,
.time_constant = 2,
.time_maxerror = NTP_PHASE_LIMIT,
.time_esterror = NTP_PHASE_LIMIT,
.ntp_next_leap_sec = TIME64_MAX,
};

/**
* ntp_clear - Clears the NTP state variables
*/
void ntp_clear(void)
{
__ntp_clear(&tk_ntp_data);
}

ntp_init

1
2
3
4
5
6
7
8
9
10
static void __init ntp_init_cmos_sync(void)
{
hrtimer_setup(&sync_hrtimer, sync_timer_callback, CLOCK_REALTIME, HRTIMER_MODE_ABS);
}

void __init ntp_init(void)
{
ntp_clear();
ntp_init_cmos_sync();
}

kernel/time/sleep_timeout.c

schedule_timeout_interruptible(待分析)

schedule_timeout 睡眠直到超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static void process_timeout(struct timer_list *t)
{
struct process_timer *timeout = timer_container_of(timeout, t, timer);

wake_up_process(timeout->task);
}

/**
* schedule_timeout - 睡眠直到超时
* @timeout: 以jiffies为单位的超时值
*
* 使当前任务睡眠,直到@timeout个jiffies过去。
* 函数行为依赖于当前任务状态(详见set_current_state()的描述):
*
* %TASK_RUNNING - 会调用调度器,但任务根本不会睡眠。
*
* %TASK_UNINTERRUPTIBLE - 保证至少@timeout个jiffies会过去,除非
* 当前任务被显式唤醒(例如,通过wake_up_process())。
*
* %TASK_INTERRUPTIBLE - 如果有信号传递给当前任务,或者任务被显式
* 唤醒,本函数可能会提前返回。
*
* 本函数返回时,保证当前任务状态是%TASK_RUNNING。
*
* 指定一个%MAX_SCHEDULE_TIMEOUT的超时值,将会无限期地让出CPU。
* 这种情况下,返回值将是%MAX_SCHEDULE_TIMEOUT。
*
* 返回值:定时器到期时返回0,否则返回剩余的jiffies时间。
* 在所有情况下,返回值都保证是非负的。
*/
/*
* __sched: 告诉编译器本函数会调用调度器。
*/
signed long __sched schedule_timeout(signed long timeout)
{
/* 在栈上创建一个临时的定时器结构体。*/
struct process_timer timer;
unsigned long expire;

switch (timeout) {
case MAX_SCHEDULE_TIMEOUT:
/*
* 注释:这两个特殊情况是为了让调用者方便。我们可以使用
* 负值来表示无限等待,但我倾向于返回一个有效的偏移量(>=0),
* 允许调用者对返回值做任何处理。
*/
/* 如果是无限等待,直接调用schedule()让出CPU,然后跳转到出口。*/
schedule();
goto out;
default:
/*
* 注释:另一处偏执的检查。注意即使timeout是负数,返回值也会是0,
* 因为内核中不应该有代码去检查schedule_timeout()的负返回值。
* 你只会看到一个printk告诉你哪里出错了。
*/
if (timeout < 0) {
pr_err("%s: wrong timeout value %lx\n", __func__, timeout);
dump_stack();
__set_current_state(TASK_RUNNING);
goto out;
}
}

/* 计算定时器的绝对到期时间(以jiffies为单位)。*/
expire = timeout + jiffies;

/* 将定时器与当前任务关联。*/
timer.task = current;
/* 在栈上初始化定时器,设置其回调函数为process_timeout。*/
timer_setup_on_stack(&timer.timer, process_timeout, 0);
/* 设置定时器的到期时间。*/
timer.timer.expires = expire;
/* 将定时器添加到内核的全局定时器列表中。*/
add_timer(&timer.timer);

/*
* 调用调度器,主动放弃CPU。当前任务会在此处睡眠,
* 直到被定时器回调或其他事件唤醒。
*/
schedule();

/*
* 任务被唤醒后,从这里继续执行。
* 调用timer_delete_sync()来同步地删除定时器。
* 这个函数会等待可能正在其他CPU上运行的定时器回调执行完毕,
* 从而安全地移除定时器,避免竞争条件。
*/
timer_delete_sync(&timer.timer);

/* 从对象追踪器中移除该栈上定时器。*/
// timer_destroy_on_stack(&timer.timer);

/* 计算剩余的超时时间。*/
timeout = expire - jiffies;

out:
/* 确保返回值永远是非负的。*/
return timeout < 0 ? 0 : timeout;
}
/* 将该符号导出,使得内核模块可以使用。*/
EXPORT_SYMBOL(schedule_timeout);