系统重启-Linux内核源码阅读

关于OS课程的内核系统调用实验题目,需要阅读系统重启、计时器相关内核源码,主要参考网站:

https://elixir.bootlin.com/linux/v5.4.218/C/ident/reboot

reboot函数

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
//magic数:用于防止误操作
//cmd:字符串,用于指定reboot方式
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
//获取当前进程命名空间
//task_active_pid_ns函数
struct pid_namespace *pid_ns = task_active_pid_ns(current);
char buffer[256];
int ret = 0;

//判断权限

//判断该任务的ns中的用户ns是否拥有CAP_SYS_BOOT的权限
/*CAP_SYS_BOOT 表示允许使用 reboot() */
//#define EPERM 1 /* 允许操作 */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;

//magic number:魔数,这是放在linux的目录中的文件信息块中的一个标识符,一般只有几位,用来标识该文件是什么类型的文件,可以被什么样的应用使用。
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
//#define EINVAL 22 /* 无效参数 */

/*重启ns,成功时返回0
* If pid namespaces are enabled and the current task is in a child
* pid_namespace, the command is handled by reboot_pid_ns() which will
* call do_exit().
*/
ret = reboot_pid_ns(pid_ns, cmd);
if (ret)
return ret;

/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
//* LINUX_REBOOT_CMD_POWER_OFF 如果可能,停止操作系统并断开系统的所有电源。
//* LINUX_REBOOT_CMD_HALT 停止操作系统并将系统控制权交给 ROM 监视器(如果有)。


mutex_lock(&reboot_mutex);//获取互斥信号量


switch (cmd) {
//RESTART 使用默认命令和模式重新启动系统。
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;

//CAD_ON Ctrl-Alt-Del 序列导致 RESTART 命令。
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;

//CAD_OFF Ctrl-Alt-Del 序列将 SIGINT 发送到 init 任务。
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;

//HALT 停止操作系统并将系统控制权交给 ROM 监视器(如果有)。
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");

//POWER_OFF 如果可能,停止操作系统并断开系统的所有电源。
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
do_exit(0);
break;

//RESTART2 使用给定的命令字符串重新启动系统。
case LINUX_REBOOT_CMD_RESTART2:
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;/* 错误地址 */
break;
}
buffer[sizeof(buffer) - 1] = '\0';

kernel_restart(buffer);
break;

#ifdef CONFIG_KEXEC_CORE

//KEXEC 使用先前加载的 Linux 内核重新启动系统
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif

#ifdef CONFIG_HIBERNATION
//SW_SUSPEND 使用软件挂起系统。
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif

default:
ret = -EINVAL;
break;
}
mutex_unlock(&reboot_mutex);
return ret;
}

task_active_pid_ns()

1
2
3
4
5
6
7
8
//获取任务的命名空间信息

//命名空间,namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。
struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
{
//首先调用函数task_pid()获得任务字段pids[PIDTYPE_PID]的pid值,然后调用函数ns_of_pid()获取函数task_pid()返回值的命名空间信息。
return ns_of_pid(task_pid(tsk));
}

ns_capable()

1
2
3
4
5
//判断ns是否有需要的权限
bool ns_capable(struct user_namespace *ns, int cap)
{
return ns_capable_common(ns, cap, CAP_OPT_NONE);
}

ns_capable_common()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//判断ns是否有指定的权限
//cap:指定的权限(宏)
//opts:指定函数的其他选项和行为
//例如: 0:只检查当前ns
// CHECK_ANCESTORS:还检查祖先进程的ns
static bool ns_capable_common(struct user_namespace *ns,
int cap,
unsigned int opts)
{
int capable;
//unlikely:表示执行else的可能性更大,方便编译减少指令跳转
if (unlikely(!cap_valid(cap))) {//cap_valid宏定义
pr_crit("capable() called with invalid cap=%u\n", cap);//pr_crit宏定义
BUG();//BUG宏定义
}
//security_capable函数
capable = security_capable(current_cred(), ns, cap, opts);
if (capable == 0) {
//如果有对应权限,在task上设置PF_SUPERPRIV(超级权限)
current->flags |= PF_SUPERPRIV;
return true;
}
return false;
}
宏BUG()
1
2
3
4
5
6
//打印BUG日志:文件名、行号、函数名
#define BUG() do { \
pr_warn("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
barrier_before_unreachable(); //防止程序继续执行的安全措施 \
__builtin_trap(); //内置函数,使程序中断 \
} while (0)
宏pr_crit()
1
2
3
4
5
6
//打印信息
//KERN_CRIT:指示严重的错误情况
//pr_fmt(fmt):格式化字符串参数
//##__VA_ARGS__:可变参数
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
宏cap_valid()
1
2
//cap_valid用来检查 cap所表示的权能是否在内核事先定义的权限范围内. 真正的权能检查是在security_capable中
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
security_capable()
1
2
3
4
5
6
7
8
9
//判断特定进程是否具有执行特定操作所需的能力
//struct cred 对象包含有关进程的有效、真实和保存的用户和组 ID 及其功能的信息。
int security_capable(const struct cred *cred,
struct user_namespace *ns,
int cap,
unsigned int opts)
{
return call_int_hook(capable, 0, cred, ns, cap, opts);//钩子函数调用capable()
}
宏call_int_hook()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
///security/security.c
//调用注册的权限检查算法的具体实现函数,依次调用安装到钩子上的安全模块函数进行检查
//FUNC:调用的函数
//IRC:保存函数的返回值
#define call_int_hook(FUNC, IRC, ...) ({ \
int RC = IRC; \
do { \
struct security_hook_list *P; \
\
//hlist_for_each_entry访问hash链表
//LSM 中使用security_hook_heads 将各访问控制模块的接口管理起来
hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
RC = P->hook.FUNC(__VA_ARGS__); \//调用钩子上的安全模块函数
if (RC != 0) \
break; \
} \
} while (0); \
RC; \
})

reboot_pid_ns()

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
int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
{
if (pid_ns == &init_pid_ns)//根ns
return 0;
//非根ns的情况
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART2:
case LINUX_REBOOT_CMD_RESTART:
pid_ns->reboot = SIGHUP;//用于指示终端已断开连接
break;

case LINUX_REBOOT_CMD_POWER_OFF:
case LINUX_REBOOT_CMD_HALT:
pid_ns->reboot = SIGINT;//终止进程信号
break;
default:
return -EINVAL;//表示将无效参数传递给函数时发生的错误情况。
}

read_lock(&tasklist_lock);//获取共享锁
send_sig(SIGKILL, pid_ns->child_reaper, 1);//发送信号停止进程
read_unlock(&tasklist_lock);//释放共享锁

do_exit(0);

/* Not reached */
return 0;
}

read_lock()

宏,定义为:__raw_read_lock(rwlock_t *lock)

1
2
3
4
5
6
7
8
9
10
//read_lock() 函数用于获取 tasklist_lock 变量上的共享锁。共享锁允许多个线程或进程同时读取共享资源,但它会阻止其中任何一个线程或进程修改资源,直到锁被释放
static inline void __raw_read_lock(rwlock_t *lock)
{
preempt_disable();//禁用内核抢占
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);//获取锁

//LOCK_CONTENDED() 宏用于处理锁当前被另一个任务持有并且调用任务无法获取锁的情况。该宏可以使用一个循环来实现,该循环反复尝试使用提供的函数指针获取锁,直到成功为止。
//do_raw_read_trylock 是一个函数指针,被调用以尝试获取锁。第三个参数 do_raw_read_lock 是一个函数指针,如果 do_raw_read_trylock 函数失败,将调用它来获取锁。
LOCK_CONTENDED(lock, do_raw_read_trylock, do_raw_read_lock);
}
preempt_disable()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//禁用内核抢占
void preempt_disable(void)
{ //如果传递给 BUG_ON 的条件为真,内核将打印一条错误消息并停止系统。
//preempt_disable_count:禁用内核抢占的次数
BUG_ON(preempt_disable_count < 0 || preempt_disable_count == INT_MAX);

//确保多次调用 preempt_disable() 不会导致抢占锁被多次获取
if (preempt_disable_count++)
return;

thread_cpu_id = nondet_int();//设定一个不确定的整数
//指定thread_cpu_id必须>=0且小于CPU总数
assume(thread_cpu_id >= 0);
assume(thread_cpu_id < NR_CPUS);//NR_CPUS:用于指定特定系统或内核旨在支持的最大 CPU 数量
//为指定的cpu获取抢占锁,防止任何在同一 CPU 上运行的任务在锁被释放之前被抢占。
//cpu_preemption_locks:数组,数组的每个元素都对应一个特定的 CPU,并持有一个锁,可以获取该锁以防止在该 CPU 上运行的任务被抢占。
lock_impl_lock(&cpu_preemption_locks[thread_cpu_id]);
}
rwlock_acquire_read()

宏,定义为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
int trylock, int read, int check,
struct lockdep_map *nest_lock, unsigned long ip)
{
unsigned long flags;

//如果 lockdep_recursion 的值为非零,则表明该函数当前正在以递归方式执行,可能是因为它多次获取了同一个锁。
if (unlikely(current->lockdep_recursion))
return;

raw_local_irq_save(flags);//保存标志寄存器值和状态标志,防止中断干扰锁操作
check_flags(flags);//确保标志寄存器有效值

current->lockdep_recursion = 1;//跟踪当前函数递归级别
trace_lock_acquire(lock, subclass, trylock, read, check, nest_lock, ip);//跟踪锁的获取时间
__lock_acquire(lock, subclass, trylock, read, check,
irqs_disabled_flags(flags), nest_lock, ip, 0, 0);//获取锁
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);//恢复标志寄存器
}
__lock_acquire()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
lock:指向 struct lockdep_map 的指针,它指定要获取的锁。
subclass:一个无符号整数,指定锁的子类。
trylock:一个标志,指定函数是应该尝试获取锁还是等到它可用。
read:标志,指定锁是读锁还是写锁。
check:一个标志,指定函数在获取锁之前是否应该执行各种检查。
hardirqs_off:一个标志,指定当前是否禁用硬中断。
nest_lock:指向指定嵌套锁的 struct lockdep_map 的指针。
ip:一个无符号长整型,指定调用函数的指令指针。
references:一个整数,指定对锁的引用数。
pin_count:一个整数,指定锁的 pin 计数。
*/
static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
int trylock, int read, int check, int hardirqs_off,
struct lockdep_map *nest_lock, unsigned long ip,
int references, int pin_count)

send_sig()

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
//向进程发送信号
//sig:一个整数,指定要发送的信号。
//p:指向 struct task_struct 的指针,它指定信号要发送到的进程
//priv:一个整数,指定信号是否从特权进程发送。
int send_sig(int sig, struct task_struct *p, int priv)
{
return send_sig_info(sig, __si_special(priv), p);
}


//info:指向 struct kernel_siginfo 的指针,它指定有关信号的附加信息。
int send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p)
{
/*
* Make sure legacy kernel users don't send in bad values
* (normal paths check this in check_kill_permission).
*/
//确保信号有效
if (!valid_signal(sig))
return -EINVAL;

return do_send_sig_info(sig, info, p, PIDTYPE_PID);
}

//type:类型为 enum pid_type 的枚举值,指定在 p 参数中传递的 PID 类型。
int do_send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p,
enum pid_type type)
{
unsigned long flags;
int ret = -ESRCH;//ESRCH 是预定义的错误代码,表示找不到请求的进程或资源。

//调用 lock_task_sighand() 函数为 p 指定的进程获取 sighand 锁。 sighand 锁是一个自旋锁,用于保护进程的信号处理数据结构。如果无法获取锁,则函数返回 -ESRCH 以指示未找到该进程。
if (lock_task_sighand(p, &flags)) {
ret = send_signal(sig, info, p, type);
unlock_task_sighand(p, &flags);
//函数调用send_signal()函数发送信号。 send_signal() 函数被传递给 sig、info、p 和类型参数。发送信号后,该函数使用 unlock_task_sighand() 函数释放 sighand 锁并返回 ret 的值。
}

return ret;
}

read_unlock()

宏,定义为_raw_read_unlock

1
2
3
4
5
6
7
8
9
10
11
12
void __lockfunc _raw_read_unlock(rwlock_t *lock)
{
__raw_read_unlock(lock);
}

static inline void __raw_read_unlock(rwlock_t *lock)
{
rwlock_release(&lock->dep_map, 1, _RET_IP_);//跟踪锁及其依赖关系,进行检查
do_raw_read_unlock(lock);//释放读写锁
preempt_enable();//允许抢占
}

rwlock_release()

宏,定义为lock_release

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
struct lockdep_map *lock:这是指向 lockdep_map 结构的指针,该结构是 Linux 内核的锁调试工具(称为“lockdep”)用来跟踪锁及其依赖关系的数据结构。它允许 lockdep 检测潜在的死锁和内核中锁使用的其他问题。
int nested:这是一个标志,表示锁是否正在从嵌套上下文中释放(即锁是否已被同一线程多次获取)。
unsigned long ip:这是释放锁的指令的指令指针(内存地址)。
*/
void lock_release(struct lockdep_map *lock, int nested,
unsigned long ip)
{
unsigned long flags;

if (unlikely(current->lockdep_recursion))
return;

raw_local_irq_save(flags);//保存标志寄存器
check_flags(flags);//检查标志
current->lockdep_recursion = 1;//指示该线程正在释放锁,此标志用于防止递归锁释放
trace_lock_release(lock, ip);//调用trace_lock_release函数跟踪锁的释放
if (__lock_release(lock, ip))//调用__lock_release函数真正释放锁
check_chain_key(current);//如果锁被释放,则调用 check_chain_key 函数对锁执行一些额外的检查。
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);//恢复标志
}
do_raw_read_unlock()
1
2
3
4
5
6
7
8
9
10
//释放 rwlock(读写器锁)上的读锁。
void do_raw_read_unlock(rwlock_t *lock)
{
//检查 rwlock_t 的magic数,以确保它是一个有效的锁。
//RWLOCK_BUG_ON 宏用于打印错误消息并在 magic 字段无效时停止内核。
RWLOCK_BUG_ON(lock->magic != RWLOCK_MAGIC, lock, "bad magic");

//调用 arch_read_unlock 函数来释放锁。 arch_read_unlock 函数是一个特定于体系结构的函数,它执行实际的解锁操作。
arch_read_unlock(&lock->raw_lock);
}

mutex_lock()

1
2
3
4
5
6
7
void __sched mutex_lock(struct mutex *lock)
{
might_sleep();//确保互斥锁操作不会导致任务在不允许的情况下进入睡眠状态

if (!__mutex_trylock_fast(lock))//调用__mutex_trylock_fast 函数尝试获取互斥锁。如果函数返回非零值,则表示成功获取锁。
__mutex_lock_slowpath(lock);//如果__mutex_trylock_fast函数返回0,这意味着锁不可用。调用 __mutex_lock_slowpath 函数使当前任务进入睡眠状态,等待锁变得可用,然后在获得锁的时候唤醒任务。
}

might_sleep()宏

1
2
3
4
5
6
//用于指示 Linux 内核中的当前任务可能会休眠(即进入低功耗状态或等待锁或其他资源可用)。
# define might_sleep() \
do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)
// __might_sleep 函数:记录当前任务的信息和源代码中可能发生睡眠的位置。
//might_resched 函数:检查当前任务是否需要重新安排并在必要时重新安排。
/**

kernel_restart()

1
2
3
4
5
6
7
8
9
10
11
12
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);//重启准备
migrate_to_reboot_cpu();//将当前任务迁移到负责重启系统的CPU。
syscore_shutdown();//关闭系统核心设备和子系统。
if (!cmd)
pr_emerg("Restarting system\n");
else
pr_emerg("Restarting system with command '%s'\n", cmd);//向内核日志打印消息
kmsg_dump(KMSG_DUMP_RESTART);//调用 kmsg_dump 函数将内核日志转储到控制台或持久存储设备。
machine_restart(cmd);//执行实际的系统重启操作
}

kernel_restart_prepare()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void kernel_restart_prepare(char *cmd)
{
//通知已注册的重启通知程序即将重启。
//reboot_notifier_list:已注册通知器列表
//SYS_RESTART 参数表示系统正在重启
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);

system_state = SYSTEM_RESTART;//将状态变量设置为 SYSTEM_RESTART,表示系统正在重启。

//禁用用户模式助手 (UMH) 进程
//UMH 进程用于从内核执行用户空间程序,它通常在系统关闭或重新启动期间被禁用,以防止它启动新程序。
usermodehelper_disable();

//关闭系统中的所有设备。此函数可以执行诸如注销设备、禁用设备电源管理和刷新设备缓冲区等任务。
device_shutdown();
}

migrate_to_reboot_cpu()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//迁移任务到reboot指定cpu中
void migrate_to_reboot_cpu(void)
{
/* The boot cpu is always logical cpu 0 */
//迁移到特定处理器或 CPU
int cpu = reboot_cpu;
//禁用热插拔(从正在运行的系统中添加或删除 CPU 的能力)
cpu_hotplug_disable();

/* Make certain the cpu I'm about to reboot on is online */
//检查 reboot_cpu 变量中指定的 CPU 是否在线(能够处理任务)。如果不在线,该函数将 cpu 变量设置为系统中第一个在线的 CPU。
if (!cpu_online(cpu))
cpu = cpumask_first(cpu_online_mask);

/* Prevent races with other tasks migrating this task */
//标志防止任务竞争
current->flags |= PF_NO_SETAFFINITY;

/* Make certain I only run on the appropriate processor */
//确保任务将仅在指定的 CPU 上运行
set_cpus_allowed_ptr(current, cpumask_of(cpu));
}

syscore_shutdown()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//syscore_shutdown 函数通常在系统关闭或重启过程中,在所有任务终止之后,系统断电或重启之前调用。它用于确保所有系统核心操作都正确关闭,并在系统关闭之前执行任何必要的清理。
void syscore_shutdown(void)
{
struct syscore_ops *ops;
//获取互斥量,以防止其他线程修改系统核心操作列表。
mutex_lock(&syscore_ops_lock);

//以相反的顺序遍历系统核心操作列表,并为每个操作调用关闭函数
list_for_each_entry_reverse(ops, &syscore_ops_list, node)
if (ops->shutdown) {
if (initcall_debug)
pr_info("PM: Calling %pS\n", ops->shutdown);
ops->shutdown();
}

mutex_unlock(&syscore_ops_lock);
}
syscore_ops
1
2
3
4
5
6
struct syscore_ops {
struct list_head node;//用于链表控制,注册和删除syscore对象时操作此链表完成
int (*suspend)(void);//睡眠流程时回调函数
void (*resume)(void);//唤醒流程时回调函数
void (*shutdown)(void);//这一级别的回调函数主要用于系统级的重启、停止或者掉电时才会使用
};

machine_restart

1
2
3
4
void machine_restart(char *cmd)
{
machine_ops.restart(cmd);
}
machine_ops
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//指向内核需要执行的各种特定于硬件的操作的函数指针。
//是从独立代码进入x86硬件的入口指针
struct machine_ops {
void (*restart)(char *cmd);
void (*halt)(void);
void (*power_off)(void);
void (*shutdown)(void);
void (*crash_shutdown)(struct pt_regs *);
void (*emergency_restart)(void);
};
struct machine_ops machine_ops = {
.power_off = native_machine_power_off,
.shutdown = native_machine_shutdown,
.restart = native_machine_restart,
.halt = native_machine_halt,
#ifdef CONFIG_KEXEC
.crash_shutdown = native_machine_crash_shutdown,
#endif
};

kernel_halt()

1
2
3
4
5
6
7
8
9
void kernel_halt(void)
{
kernel_shutdown_prepare(SYSTEM_HALT);//重启准备
migrate_to_reboot_cpu();//任务迁移到负责reboot的cpu
syscore_shutdown();//关闭系统核心设备和子系统。
pr_emerg("System halted\n");
kmsg_dump(KMSG_DUMP_HALT);//日志转储
machine_halt();
}

kernel_power_off()

1
2
3
4
5
6
7
8
9
10
11
void kernel_power_off(void)
{
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("Power down\n");
kmsg_dump(KMSG_DUMP_POWEROFF);
machine_power_off();
}

kernel_shutdown_prepare()

1
2
3
4
5
6
7
8
static void kernel_shutdown_prepare(enum system_states state)
{
blocking_notifier_call_chain(&reboot_notifier_list,
(state == SYSTEM_HALT) ? SYS_HALT : SYS_POWER_OFF, NULL);
system_state = state;
usermodehelper_disable();
device_shutdown();
}

native_machine_power_off()

1
2
3
4
5
6
7
8
9
10
11
static void native_machine_power_off(void)
{
if (pm_power_off) {
if (!reboot_force)
machine_shutdown();
pm_power_off();
}
/* A fallback in case there is no PM info available */
tboot_shutdown(TB_SHUTDOWN_HALT);
}

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
void native_machine_shutdown(void)
{
/* Stop the cpus and apics */
#ifdef CONFIG_X86_IO_APIC
/*
* Disabling IO APIC before local APIC is a workaround for
* erratum AVR31 in "Intel Atom Processor C2000 Product Family
* Specification Update". In this situation, interrupts that target
* a Logical Processor whose Local APIC is either in the process of
* being hardware disabled or software disabled are neither delivered
* nor discarded. When this erratum occurs, the processor may hang.
*
* Even without the erratum, it still makes sense to quiet IO APIC
* before disabling Local APIC.
*/
//clear_IO_APIC() 函数是一个清除或重置系统上输入/输出高级可编程中断控制器 (IO-APIC) 状态的函数。 IO-APIC 是一种用于将中断请求 (IRQ) 从设备路由到系统中适当的处理器或内核的设备。
clear_IO_APIC();
#endif

#ifdef CONFIG_SMP
/*
* Stop all of the others. Also disable the local irq to
* not receive the per-cpu timer interrupt which may trigger
* scheduler's load balance.
*/
local_irq_disable();//禁用中断
stop_other_cpus();//停止系统中的所有其他 CPU(中央处理器)使用对称多处理 (SMP)
#endif

lapic_shutdown();// lapic_shutdown() 函数,关闭系统上的本地 APIC(高级可编程中断控制器)
restore_boot_irq_mode();//将系统的中断请求 (IRQ) 模式恢复为系统启动时的模式。

#ifdef CONFIG_HPET_TIMER
hpet_disable();//禁用高精度事件计时器 (HPET)。
#endif

#ifdef CONFIG_X86_64
x86_platform.iommu_shutdown();//平台重启
#endif
}

kernel_kexec()

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
//通常用于在发生内核恐慌或其他灾难性故障时执行系统的紧急重启。
int kernel_kexec(void)
{
int error = 0;
//获取一个互斥量,以防止发生多个并发的 kexec 重启
if (!mutex_trylock(&kexec_mutex))
return -EBUSY;
if (!kexec_image) {
error = -EINVAL;
goto Unlock;
}

#ifdef CONFIG_KEXEC_JUMP
//查是否已设置 kexec 映像,如果没有则返回错误
if (kexec_image->preserve_context) {
pm_prepare_console();
error = freeze_processes();
if (error) {
error = -EBUSY;
goto Restore_console;
}
suspend_console();
//在 dpm_suspend_start() 之后调用 dpm_suspend_end() 有助于将设备驱动程序与硬件的实际状态同步。
error = dpm_suspend_start(PMSG_FREEZE);
if (error)
goto Resume_console;
/* At this point, dpm_suspend_start() has been called,
* but *not* dpm_suspend_end(). We *must* call
* dpm_suspend_end() now. Otherwise, drivers for
* some devices (e.g. interrupt controllers) become
* desynchronized with the actual state of the
* hardware at resume time, and evil weirdness ensues.
*/
error = dpm_suspend_end(PMSG_FREEZE);
//通过冻结进程、禁用辅助 CPU 和挂起设备来为系统重启做好准备。
if (error)
goto Resume_devices;
error = suspend_disable_secondary_cpus();
if (error)
goto Enable_cpus;
local_irq_disable();//禁用中断
error = syscore_suspend();
if (error)
goto Enable_irqs;
} else
#endif
{ //reboot
kexec_in_progress = true;
kernel_restart_prepare(NULL);
migrate_to_reboot_cpu();

/*
* migrate_to_reboot_cpu() disables CPU hotplug assuming that
* no further code needs to use CPU hotplug (which is true in
* the reboot case). However, the kexec path depends on using
* CPU hotplug again; so re-enable it here.
*/
cpu_hotplug_enable();
pr_emerg("Starting new kernel\n");
machine_shutdown();
}

machine_kexec(kexec_image);

#ifdef CONFIG_KEXEC_JUMP
//用于在系统挂起、恢复和关闭期间需要完成的系统范围的操作。它被调用以恢复系统核心状态。
if (kexec_image->preserve_context) {
syscore_resume();
Enable_irqs:
local_irq_enable();//在当前 CPU 上启用本地中断
Enable_cpus:
suspend_enable_secondary_cpus();//挂起后启用辅助 CPU
dpm_resume_start(PMSG_RESTORE);
Resume_devices:
dpm_resume_end(PMSG_RESTORE);//在系统挂起或休眠操作后调用它来启动恢复设备的过程。
Resume_console:
resume_console();//在系统挂起或休眠操作后恢复控制台状态。
thaw_processes();//函数解冻(解冻)之前在系统挂起或休眠操作期间冻结的所有进程。
Restore_console:
pm_restore_console();//在系统挂起或休眠操作后恢复控制台状态。

}
#endif

Unlock:
mutex_unlock(&kexec_mutex);//释放互斥量
return error;
}

mutex_unlock()

1
2
3
4
5
6
7
8
void __sched mutex_unlock(struct mutex *lock)
{
#ifndef CONFIG_DEBUG_LOCK_ALLOC
if (__mutex_unlock_fast(lock))
return;
#endif
__mutex_unlock_slowpath(lock, _RET_IP_);
}

hibernate()

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
/**
* hibernate - Carry out system hibernation, including saving the image.
*/
//电源休眠
int hibernate(void)
{
int error, nr_calls = 0;
bool snapshot_test = false;

//检查系统是否可以休眠,如果不是则返回错误
if (!hibernation_available()) {
pm_pr_dbg("Hibernation not available.\n");
return -EPERM;
}

lock_system_sleep();
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
error = -EBUSY;
goto Unlock;
}

//调用 PM_HIBERNATION_PREPARE 通知程序链,让设备和其他子进程准备休眠。
pr_info("hibernation entry\n");
pm_prepare_console();
error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
if (error) {
nr_calls--;
goto Exit;
}

ksys_sync_helper();
//调用 freeze_processes() 来冻结系统上的所有进程
error = freeze_processes();
if (error)
goto Exit;

//锁定设备热插拔互斥锁以防止任何进一步的设备热插拔操作。(从正在运行的系统中添加或删除 CPU 的能力)
lock_device_hotplug();
/* Allocate memory management structures */
//分配内存管理结构并获取系统内存的快照,将其保存到文件或设备中。
error = create_basic_memory_bitmaps();
if (error)
goto Thaw;
//调用 hibernation_snapshot() 来创建系统当前状态的快照
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error || freezer_test_done)
goto Free_bitmaps;

//如果系统正在挂起,该函数使用 swsusp_write() 将快照映像写入磁盘并关闭系统。
if (in_suspend) {
unsigned int flags = 0;

if (hibernation_mode == HIBERNATION_PLATFORM)
flags |= SF_PLATFORM_MODE;
if (nocompress)
flags |= SF_NOCOMPRESS_MODE;
else
flags |= SF_CRC32_MODE;

pm_pr_dbg("Writing image.\n");
error = swsusp_write(flags);
swsusp_free();
if (!error) {
if (hibernation_mode == HIBERNATION_TEST_RESUME)
snapshot_test = true;
else
power_down();
}
in_suspend = 0;
pm_restore_gfp_mask();
} else {
pm_pr_dbg("Image restored successfully.\n");
}

Free_bitmaps:
free_basic_memory_bitmaps();
Thaw:
unlock_device_hotplug();
//如果系统正在恢复,该函数使用 load_image_and_restore() 从快照映像恢复系统。
if (snapshot_test) {
pm_pr_dbg("Checking hibernation image\n");
error = swsusp_check();
if (!error)
error = load_image_and_restore();
}
thaw_processes();

/* Don't bother checking whether freezer_test_done is true */
//释放内存管理结构、解冻进程并向所有已注册的通知程序发送 PM_POST_HIBERNATION 通知来进行清理。
freezer_test_done = false;
Exit:
__pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
pm_restore_console();
atomic_inc(&snapshot_device_available);
Unlock:
//释放系统睡眠锁并返回。
unlock_system_sleep();
pr_info("hibernation exit\n");

return error;
}

emergency_restart()

1
2
3
4
5
6
7
8
void  emergency_restart ( void ) 
{
//kmsg_dump 是一个函数,用于将内核日志缓冲区转储到控制台或持久存储设备。
//KMSG_DUMP_EMERG 宏作为参数传递,表示由于紧急情况正在转储日志。
kmsg_dump ( KMSG_DUMP_EMERG );
//启动系统的紧急重启
machine_emergency_restart ();
}

kmsg_dump()

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
void kmsg_dump(enum kmsg_dump_reason reason)
{
struct kmsg_dumper *dumper;
unsigned long flags;

//检查指定的原因是否越界以及是否设置了 always_kmsg_dump 标志。
//always_kmsg_dump的值可以设置为 1 以允许在所有情况下转储内核日志缓冲区,或者设置为 0 以禁用内核日志缓冲区的转储
if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
return;

//在使用 RCU(读取-复制更新)同步的程序中获取读取端临界区,防止并发访问和修改
rcu_read_lock();
//遍历已注册的“转储器”结构列表
list_for_each_entry_rcu(dumper, &dump_list, list) {
if (dumper->max_reason && reason > dumper->max_reason)
continue;

/* initialize iterator with data about the stored records */
dumper->active = true;
//初始化迭代器
logbuf_lock_irqsave(flags);
dumper->cur_seq = clear_seq;
dumper->cur_idx = clear_idx;
dumper->next_seq = log_next_seq;
dumper->next_idx = log_next_idx;
logbuf_unlock_irqrestore(flags);


/* invoke dumper which will iterate over records */
//调用转储器
dumper->dump(dumper, reason);

/* reset iterator */
dumper->active = false;
}
rcu_read_unlock();//关闭RCU临界区
}

machine_emergency_restart()

1
2
3
4
5
6
7
8
9
10
11
12
13
void machine_emergency_restart(void)
{
__machine_emergency_restart(1);
}

//int emergency:紧急情况的类型
static void __machine_emergency_restart(int emergency)
{
//设置reboot状态
reboot_emergency = emergency;
//调用 machine_ops 结构的 emergency_restart 函数,这是一个特定于平台的结构,包含指向内核需要执行的各种特定于硬件的操作的函数指针。 emergency_restart 函数负责启动实际的重启过程,这可能涉及关闭系统、重置硬件组件或执行其他特定于平台的操作。
machine_ops.emergency_restart();
}

machine_ops()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//指向内核需要执行的各种特定于硬件的操作的函数指针。
//是从独立代码进入x86硬件的入口指针
struct machine_ops {
void (*restart)(char *cmd);
void (*halt)(void);
void (*power_off)(void);
void (*shutdown)(void);
void (*crash_shutdown)(struct pt_regs *);
void (*emergency_restart)(void);
};
struct machine_ops machine_ops __ro_after_init = {
.power_off = native_machine_power_off,
.shutdown = native_machine_shutdown,
.emergency_restart = native_machine_emergency_restart,
.restart = native_machine_restart,
.halt = native_machine_halt,
#ifdef CONFIG_KEXEC_CORE
.crash_shutdown = native_machine_crash_shutdown,
#endif
};
native_machine_emergency_restart()
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
static void native_machine_emergency_restart(void)
{
int i;
int attempt = 0;
int orig_reboot_type = reboot_type;
unsigned short mode;

//禁用系统中的所有虚拟机扩展(vmx)
if (reboot_emergency)
emergency_vmx_disable_all();

//trust boot重启
tboot_shutdown(TB_SHUTDOWN_REBOOT);

/* Tell the BIOS if we want cold or warm reboot */
//重启方式
mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
*((unsigned short *)__va(0x472)) = mode;

/*
* If an EFI capsule has been registered with the firmware then
* override the reboot= parameter.
*/
//检查可扩展固件接口 (EFI) 封装是否已在固件中注册,如果已注册,则强制系统使用 EFI 引导过程重新引导。
//EFI capsule 是一种数据结构,包含固件更新或其他可以在引导过程中传递给系统固件的信息。
if (efi_capsule_pending(NULL)) {
pr_info("EFI capsule is pending, forcing EFI reboot.\n");
reboot_type = BOOT_EFI;
}

//reboot_type通常在其他函数中设定,如reboot_setup中等
for (;;) {
/* Could also try the reset bit in the Hammer NB */
switch (reboot_type) {

//BOOT_ACPI:指示系统应尝试使用高级配置和电源接口 (ACPI) 标准重新启动。
case BOOT_ACPI:
acpi_reboot();
reboot_type = BOOT_KBD;
break;

//BOOT_KBD:表示系统应该尝试使用键盘控制器对复位线发出脉冲来重新启动。
case BOOT_KBD:
mach_reboot_fixups(); /* For board specific fixups */

for (i = 0; i < 10; i++) {
kb_wait();
udelay(50);
outb(0xfe, 0x64); /* Pulse reset low */
udelay(50);
}
if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {
attempt = 1;
reboot_type = BOOT_ACPI;
} else {
reboot_type = BOOT_EFI;
}
break;

//BOOT_EFI:指示系统应尝试使用可扩展固件接口 (EFI) 引导过程重新引导。
case BOOT_EFI:
efi_reboot(reboot_mode, NULL);
reboot_type = BOOT_BIOS;
break;

//BOOT_BIOS:指示系统应尝试使用 BIOS 引导过程重新引导。
case BOOT_BIOS:
machine_real_restart(MRR_BIOS);

/* We're probably dead after this, but... */
reboot_type = BOOT_CF9_SAFE;
break;

//BOOT_CF9_FORCE:表示系统应该尝试通过写入 0xcf9 端口并强制重置来重启。
case BOOT_CF9_FORCE:
port_cf9_safe = true;
/* Fall through */

//BOOT_CF9_SAFE:表示系统应该尝试通过写入 0xcf9 端口来重启,但前提是这样做是安全的。
case BOOT_CF9_SAFE:
if (port_cf9_safe) {
u8 reboot_code = reboot_mode == REBOOT_WARM ? 0x06 : 0x0E;
u8 cf9 = inb(0xcf9) & ~reboot_code;
outb(cf9|2, 0xcf9); /* Request hard reset */
udelay(50);
/* Actually do the reset */
outb(cf9|reboot_code, 0xcf9);
udelay(50);
}
reboot_type = BOOT_TRIPLE;
break;

//BOOT_TRIPLE:表示系统应该尝试通过生成三重故障来重启
case BOOT_TRIPLE:
idt_invalidate(NULL);
__asm__ __volatile__("int3");

/* We're probably dead after this, but... */
reboot_type = BOOT_KBD;
break;
}
}
}
acpi_reboot()
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
//用ACPI方式重启
void acpi_reboot(void)
{
struct acpi_generic_address *rr;
u8 reset_value;

//检查系统是否禁用了 ACPI
if (acpi_disabled)
return;

rr = &acpi_gbl_FADT.reset_register;

/* ACPI reset register was only introduced with v2 of the FADT */

// FADT 中的 header.revision 字段指定了表的版本。如果 header.revision 字段小于 2,则意味着 reset_register 字段不存在于 FADT 中,因此不能用于重置系统。
if (acpi_gbl_FADT.header.revision < 2)
return;

/* Is the reset register supported? The spec says we should be
* checking the bit width and bit offset, but Windows ignores
* these fields */
//检索指向 ACPI 重置寄存器的指针并检查它是否受支持
if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER))
return;

reset_value = acpi_gbl_FADT.reset_value;

//确定重置寄存器的类型(I/O、内存或 PCI 配置空间)并调用适当的函数来启动重启。
/* The reset register can only exist in I/O, Memory or PCI config space
* on a device on bus 0. */
//如果重置寄存器位于 PCI 配置空间中,则该函数会调用 acpi_pci_reboot() 函数来启动重启。
switch (rr->space_id) {
case ACPI_ADR_SPACE_PCI_CONFIG:
acpi_pci_reboot(rr, reset_value);
break;

//如果重置寄存器在系统内存或 I/O 空间中,则该函数调用 acpi_reset() 函数来启动重启。
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
case ACPI_ADR_SPACE_SYSTEM_IO:
printk(KERN_DEBUG "ACPI MEMORY or I/O RESET_REG.\n");
acpi_reset();
break;
}
}
efi_reboot()
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
//efi_reboot() 函数用于使用可扩展固件接口 (EFI) 标准启动系统重启。 EFI 是一种规范,它定义了一组接口和协议,可用于与计算机系统的固件进行交互。
void efi_reboot(enum reboot_mode reboot_mode, const char *__unused)
{
const char *str[] = { "cold", "warm", "shutdown", "platform" };
int efi_mode, cap_reset_mode;

//检查系统是否启用了 EFI 运行时服务,如果没有,它会立即返回
if (!efi_enabled(EFI_RUNTIME_SERVICES))
return;

//reboot_mode,指定要执行的重启类型(冷、热)
//热启动没有关闭电源;启动过程的区别是,冷启动需要进行硬件的自检,而热启动跳过硬件自检这一步。
switch (reboot_mode) {
case REBOOT_WARM:
case REBOOT_SOFT:
efi_mode = EFI_RESET_WARM;
break;
default:
efi_mode = EFI_RESET_COLD;
break;
}

/*
* If a quirk forced an EFI reset mode, always use that.
*/

//如果 capsule 更新挂起,该函数检查请求的重置模式是否与挂起的 capsule 更新所需的重置模式兼容,如果不兼容,它会打印一条警告消息并相应地调整重置模式。
if (efi_reboot_quirk_mode != -1)
efi_mode = efi_reboot_quirk_mode;

if (efi_capsule_pending(&cap_reset_mode)) {
if (efi_mode != cap_reset_mode)
printk(KERN_CRIT "efi: %s reset requested but pending "
"capsule update requires %s reset... Performing "
"%s reset.\n", str[efi_mode], str[cap_reset_mode],
str[cap_reset_mode]);
efi_mode = cap_reset_mode;
}
//调用 EFI reset_system() 函数来启动重启。 reset_system() 函数是 EFI 运行时服务的一部分,由系统固件实现。
efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);
}
machine_real_restart()
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
//BIOS引导重启
void __noreturn machine_real_restart(unsigned int type)
{
//禁用中断
local_irq_disable();

/*
* Write zero to CMOS register number 0x0f, which the BIOS POST
* routine will recognize as telling it to do a proper reboot. (Well
* that's what this book in front of me says -- it may only apply to
* the Phoenix BIOS though, it's not clear). At the same time,
* disable NMIs by setting the top bit in the CMOS address register,
* as we're about to do peculiar things to the CPU. I'm not sure if
* `outb_p' is needed instead of just `outb'. Use it to be on the
* safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.)
*/
//将0x00值写入 CMOS 寄存器。 BIOS POST(开机自检)例程使用此值来确定是否应执行正确的重新引导。通过设置 CMOS 地址寄存器的最高位来禁用 NMI(不可屏蔽中断)
spin_lock(&rtc_lock);//锁
CMOS_WRITE(0x00, 0x8f);
spin_unlock(&rtc_lock);

/*
* Switch to the trampoline page table.
*/

load_trampoline_pgtable();
//跳转到执行实际系统重启的低内存代码。
//代码位于 real_mode_header->machine_real_restart_asm 地址,并传递指定要执行的重启类型的类型参数。
/* Jump to the identity-mapped low memory code */
#ifdef CONFIG_X86_32
asm volatile("jmpl *%0" : :
"rm" (real_mode_header->machine_real_restart_asm),
"a" (type));
#else
asm volatile("ljmpl *%0" : :
"m" (real_mode_header->machine_real_restart_asm),
"D" (type));
#endif
//调用无法访问的函数来标记函数的结束,因为代码不应从重启中返回。因此没有return
unreachable();
}

timer_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry;//struct hlist_node 字段,允许将计时器插入哈希列表
unsigned long expires;//存储了定时器应该到期的时间
void (*function)(struct timer_list *);//指向定时器到期时应该调用的函数的函数指针
u32 flags;
/*
IMER_CPUMASK:这个标志表示定时器应该只在一组特定的 CPU 上运行,如定时器的 cpumask 字段中指定的那样。
TIMER_PINNED:这个标志表示定时器被固定到一个特定的 CPU,即使当前 CPU 空闲也不应该迁移到另一个 CPU。
TIMER_IRQSAFE:该标志表示可以从中断上下文安全地调用定时器函数。
TIMER_ONESHOT:这个标志表示定时器应该只运行一次然后从定时器列表中删除。
TIMER_STOP:此标志用于停止计时器并防止其运行。
*/

#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;//如果启用CONFIG_LOCKDEP 选项,用于锁调试
#endif
};

timer_setup宏

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
#define timer_setup(timer, callback, flags)			\
__init_timer((timer), (callback), (flags))

#define __init_timer(_timer, _fn, _flags) \
init_timer_key((_timer), (_fn), (_flags), NULL, NULL)

void init_timer_key(struct timer_list *timer,
void (*func)(struct timer_list *), unsigned int flags,
const char *name, struct lock_class_key *key)
{

debug_init(timer);//给定定时器初始化调试
do_init_timer(timer, func, flags, name, key);
}

static void do_init_timer(struct timer_list *timer,
void (*func)(struct timer_list *),
unsigned int flags,
const char *name, struct lock_class_key *key)
{
timer->entry.pprev = NULL;
timer->function = func;
timer->flags = flags | raw_smp_processor_id();
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}

add_timer()

1
2
3
4
5
6
void add_timer(struct timer_list *timer)
{
////如果传递给 BUG_ON 的条件为真,内核将打印一条错误消息并停止系统。
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
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
int mod_timer(struct timer_list *timer, unsigned long expires)
{
return __mod_timer(timer, expires, 0);
}
//修改定时器的到期时间和行为
__mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options)
{
struct timer_base *base, *new_base;
unsigned int idx = UINT_MAX;
unsigned long clk = 0, flags;
int ret = 0;
//函数错误BUG
BUG_ON(!timer->function);

/*
* This is a common optimization triggered by the networking code - if
* the timer is re-modified to have the same timeout or ends up in the
* same array bucket then just return:
*/
//检查计时器是否已挂起,如果是,则检查新的到期时间是否与旧的相同。如果选项标志 MOD_TIMER_REDUCE 被设置并且新的到期时间早于旧的,函数也返回。
if (timer_pending(timer)) {
/*
* The downside of this optimization is that it can result in
* larger granularity than you would get from adding a new
* timer with this expiry.
*/
long diff = timer->expires - expires;

if (!diff)
return 1;
if (options & MOD_TIMER_REDUCE && diff <= 0)
return 1;

/*
* We lock timer base and calculate the bucket index right
* here. If the timer ends up in the same bucket, then we
* just update the expiry time and avoid the whole
* dequeue/enqueue dance.
*/
//检查计时器当前是否处于挂起状态,如果定时器已经挂起,函数会检查它是否已经在正确的base中。如果是,则在未设置 MOD_TIMER_REDUCE 标志的情况下更新到期时间并返回。
base = lock_timer_base(timer, &flags);
forward_timer_base(base);

//检查计时器的到期时间是否小于或等于新的到期时间。如果是这种情况,它将返回值设置为 1 并跳转到标签 out_unlock。
if (timer_pending(timer) && (options & MOD_TIMER_REDUCE) &&
time_before_eq(timer->expires, expires)) {
ret = 1;
goto out_unlock;
}

//更新clock和索引,clk和idx用于确定将定时器放置在定时器轮数据结构中的位置。
clk = base->clk;
idx = calc_wheel_index(expires, clk);

/*
* Retrieve and compare the array index of the pending
* timer. If it matches set the expiry to the new value so a
* subsequent call will exit in the expires check above.
*/
//检查计时器的索引是否与为计时器的新到期时间计算的索引相同。如果是,直接更新计时器时间
if (idx == timer_get_idx(timer)) {
if (!(options & MOD_TIMER_REDUCE))
timer->expires = expires;
else if (time_after(timer->expires, expires))
timer->expires = expires;
ret = 1;
goto out_unlock;//跳转到释放定时器基锁的函数末尾
}
} else {
base = lock_timer_base(timer, &flags);
forward_timer_base(base);
}

ret = detach_if_pending(timer, base, false);
if (!ret && (options & MOD_TIMER_PENDING_ONLY))
goto out_unlock;

new_base = get_target_base(base, timer->flags);

if (base != new_base) {
/*
* We are trying to schedule the timer on the new base.
* However we can't change timer's base while it is running,
* otherwise del_timer_sync() can't detect that the timer's
* handler yet has not finished. This also guarantees that the
* timer is serialized wrt itself.
*/
//在新的base上建立计时器
//如果计时器当前未运行,则代码会在计时器的标志字段中设置 TIMER_MIGRATING 标志,以指示计时器正在被移动到新的base。
if (likely(base->running_timer != timer)) {
/* See the comment in lock_timer_base() */
timer->flags |= TIMER_MIGRATING;

//解锁当前基地,锁定新基地,并更新计时器的标志字段以反映它属于新base。
raw_spin_unlock(&base->lock);
base = new_base;
raw_spin_lock(&base->lock);
WRITE_ONCE(timer->flags,
(timer->flags & ~TIMER_BASEMASK) | base->cpu);
forward_timer_base(base);
}
}

debug_timer_activate(timer);

timer->expires = expires;
/*
* If 'idx' was calculated above and the base time did not advance
* between calculating 'idx' and possibly switching the base, only
* enqueue_timer() and trigger_dyntick_cpu() is required. Otherwise
* we need to (re)calculate the wheel index via
* internal_add_timer().
*/
//如果基准时间没有提前,则可以在计算出的索引处将计时器添加到计时器轮,而无需重新计算它。
if (idx != UINT_MAX && clk == base->clk) {
enqueue_timer(base, timer, idx);
trigger_dyntick_cpu(base, timer);
} else {
internal_add_timer(base, timer);//internal_add_timer()重新计算idx
}

out_unlock:
raw_spin_unlock_irqrestore(&base->lock, flags);

return ret;
}

系统重启-Linux内核源码阅读
http://example.com/2022/12/31/系统重启-Linux内核源码阅读/
作者
Magnesium
发布于
2022年12月31日
许可协议