[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 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; } #define ktime_sub(lhs, rhs) ((lhs) - (rhs)) #define ktime_add(lhs, rhs) ((lhs) + (rhs)) 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 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 static inline bool timespec64_valid (const struct timespec64 *ts) { if (ts->tv_sec < 0 ) return false ; 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 ; 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 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 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 void account_process_tick (struct task_struct *p, int user_tick) { u64 cputime; u64 steal; cputime = TICK_NSEC; if (user_tick) account_user_time(p, cputime); else if ((p != this_rq()->idle) || (irq_count() != HARDIRQ_OFFSET)) account_system_time(p, HARDIRQ_OFFSET, cputime); else 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 void set_normalized_timespec64 (struct timespec64 *ts, time64_t sec, s64 nsec) { while (nsec >= NSEC_PER_SEC) { asm ("" : "+rm" (nsec)); nsec -= NSEC_PER_SEC; ++sec; } 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 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 ) { 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){ 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; 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, }; 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 msleep & schedule_timeout: 任务睡眠与延时调度 本代码片段展示了Linux内核中任务(线程)进行延时操作的核心实现。其主要功能由schedule_timeout函数族提供,它允许当前任务进入不同类型的睡眠状态(可中断、不可中断等),并设置一个定时器在指定时间后将其唤醒。msleep则是在此基础上封装的一个更易于使用的、以毫秒为单位的不可中断睡眠函数。
实现原理分析 该机制是内核调度器与内核定时器子系统协同工作的经典范例。它通过组合任务状态变更、定时器设置和调度器调用,实现了精确的、可配置的延时功能。
核心引擎 (schedule_timeout) :
这是所有延时功能的基础。其核心思想是在当前任务的栈上创建一个临时的内核定时器(struct timer_list)。
定时器设置 : 该函数计算出未来的超时时刻(expire = timeout + jiffies),并设置一个定时器。当定时器到期时,内核会调用其回调函数process_timeout。
唤醒机制 : process_timeout回调函数的唯一作用就是调用wake_up_process,将创建该定时器的任务(timer.task)的状态从睡眠态(如 TASK_UNINTERRUPTIBLE)变更为就绪态(TASK_RUNNING),使其能够被调度器再次选中执行。
放弃CPU : 设置好定时器后,函数会调用schedule()。这个调用会主动触发调度器,让出CPU给其他处于就緒态的任务。此时,由于当前任务处于睡眠状态,它不会被调度器选中,从而实现了“睡眠”。
同步与清理 : 当任务被唤醒并重新获得CPU后,它会从schedule()调用处继续执行。timer_delete_sync()是一个关键的同步函数,它确保在函数返回、栈上的定时器变量被销毁之前,该定时器已经被完全停用,并且其回调函数(process_timeout)没有也绝不会在任何CPU上运行。
睡眠状态的控制 (schedule_timeout_* 变体) :
内核中的睡眠分为多种状态,主要区别在于能否被信号唤醒。
schedule_timeout_uninterruptible: 将任务状态设置为TASK_UNINTERRUPTIBLE。在这种状态下,任务只能被显式的wake_up_process调用唤醒,完全忽略信号。这是最“深”的睡眠,用于等待必要资源且不能被用户干预的场景。
schedule_timeout_interruptible: 将任务状态设置为TASK_INTERRUPTIBLE。任务既可以被定时器到期唤醒,也可以被未屏蔽的信号提前唤醒。
schedule_timeout_killable: 任务状态为TASK_KILLABLE,是前两者的折中。它会忽略普通信号,但可以被致命信号(如SIGKILL)中断。
这些函数本质上都是在调用核心的schedule_timeout之前,先通过__set_current_state()设置好任务状态的简单封装。
高层封装 (msleep) :
这是一个对驱动开发者更友好的API,它接受毫秒作为参数。
它首先将毫秒转换为内核内部的时间单位jiffies。
它在一个while循环中调用schedule_timeout_uninterruptible。这种循环结构非常健壮,即使schedule_timeout因为某些罕见原因(虽然在uninterruptible模式下几乎不可能)提前返回,它也会重新计算剩余的超时时间,并继续睡眠,直到总的睡眠时间达到要求。
代码分析 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 void msleep (unsigned int msecs) { unsigned long timeout = msecs_to_jiffies(msecs); while (timeout) timeout = schedule_timeout_uninterruptible(timeout); } EXPORT_SYMBOL(msleep); struct process_timer { struct timer_list timer ; struct task_struct *task ; }; static void process_timeout (struct timer_list *t) { struct process_timer *timeout = timer_container_of(timeout, t, timer); wake_up_process(timeout->task); } signed long __sched schedule_timeout (signed long timeout) { struct process_timer timer ; unsigned long expire; switch (timeout) { case MAX_SCHEDULE_TIMEOUT: schedule(); goto out; default : if (timeout < 0 ) { pr_err("%s: 错误的超时值 %lx\n" , __func__, timeout); dump_stack(); __set_current_state(TASK_RUNNING); goto out; } } expire = timeout + jiffies; timer.task = current; timer_setup_on_stack(&timer.timer, process_timeout, 0 ); timer.timer.expires = expire; add_timer(&timer.timer); schedule(); timer_delete_sync(&timer.timer); timer_destroy_on_stack(&timer.timer); timeout = expire - jiffies; out: return timeout < 0 ? 0 : timeout; } EXPORT_SYMBOL(schedule_timeout); signed long __sched schedule_timeout_interruptible (signed long timeout) { __set_current_state(TASK_INTERRUPTIBLE); return schedule_timeout(timeout); } EXPORT_SYMBOL(schedule_timeout_interruptible); signed long __sched schedule_timeout_killable (signed long timeout) { __set_current_state(TASK_KILLABLE); return schedule_timeout(timeout); } EXPORT_SYMBOL(schedule_timeout_killable); signed long __sched schedule_timeout_uninterruptible (signed long timeout) { __set_current_state(TASK_UNINTERRUPTIBLE); return schedule_timeout(timeout); } EXPORT_SYMBOL(schedule_timeout_uninterruptible);