Linux Suspend主要有以下三步:
1) 冻结用户态进程和内核态任务
2) 调用注册的设备的suspend的回调函数,顺序是按照注册顺序
3) 休眠核心设备和使CPU进入休眠态。
冻结进程(suspend_freeze_processes)是内核把进程列表中所有的进程的状态都设置为停止,并且保存所有进程的上下文。 当这些进程被解冻(suspend_thaw_processes)的时候,他们是不知道自己被冻结过的,只是简单的继续执行。如何让Linux进入Suspend呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠,比如:
# echo standby > /sys/power/state
Suspend主要流程如下图所示:
其主要功能如下:
1) suspend_prepare: 准备进入suspend,并冻结所有进程
2) suspend_devices_and_enter: suspend所有外设,并进入sleep状态,只有当唤醒时,此函数才返回
3) suspend_finish: suspend结束,并被唤醒
enter_state代码如下:
在suspend_prepare()中它将完成以下任务:
1) 给suspend分配一个虚拟终端来输出信息;
2) 然后广播一个系统要进入suspend的Notify;
3) 关闭掉用户态的helper进程;
4) 最后调用suspend_freeze_processes()冻结所有的进程,这里将保存所有进程 当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程。
其详细代码如下:
现在, 所有的进程(也包括workqueue/kthread) 都已经停止了,内核态进程有可能在停止的时候握有一些信号量, 所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设的suspend()函数里面进行lock/unlock锁要非常小心,建议设计时不要在suspend()里面等待锁。而且因为suspend的时候,有一些Log是无法输出的,所以一旦出现问题,非常难调试。
suspend_devices_and_enter的主要功能为:
1) suspend_console: Suspend console子系统,即printk将不能打印了
2) dpm_suspend_start: Suspend所有非系统设备,即调用所有注册设备的suspend回调函数
3) suspend_enter: 使系统进入要求的sleep状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回
以下函数只有当wakeup时才被执行:
4) dpm_resume_end: resume所有非系统设备,即执行所有注册设备的resume回调函数
5) resume_console: resume console子系统,即printk可用了
详细代码如下所示:
kernel/kernel/power/suspend.c
Suspend console子系统,即printk将不能打印了
Suspend所有非系统设备,即调用所有注册设备的suspend回调函数
使系统进入要求的sleep状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回,其详细代码如下:
resume所有非系统设备,即执行所有注册设备的resume回调函数
resume console子系统,即printk可用了
其主要功能如下(它是suspend_prepare的逆过程):
1) 解冻所有进程;
2) 打开用户态helper进程;
3) 广播系系统suspend结束的Notify;
4) 释放分配的虚拟终端。
其详细代码如下: