您的位置:首页 > 运维架构 > Linux

Linux Suspend过程【转】

2016-06-12 17:40 627 查看
转自:http://blog.csdn.net/chen198746/article/details/15809363

目录(?)[-]

Linux Suspend简介

Suspend流程

enter_statePM_SUSPEND_MEM

31 准备并冻结进程suspend_prepare

32 Suspend外部设备suspend_devices_and_enter

21 suspend_console

22 dpm_suspend_start PMSG_SUSPEND

23 suspend_enter

24 dpm_resume_end PMSG_RESUME

25 resume_console

3 Suspend结束suspend_finish

1. Linux Suspend简介

Linux Suspend主要有以下三步:

1) 冻结用户态进程和内核态任务
2) 调用注册的设备的suspend的回调函数,顺序是按照注册顺序
3) 休眠核心设备和使CPU进入休眠态。
冻结进程(suspend_freeze_processes)是内核把进程列表中所有的进程的状态都设置为停止,并且保存所有进程的上下文。 当这些进程被解冻(suspend_thaw_processes)的时候,他们是不知道自己被冻结过的,只是简单的继续执行。如何让Linux进入Suspend呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠,比如:

# echo standby > /sys/power/state

2. Suspend流程

Suspend主要流程如下图所示:



3. enter_state(PM_SUSPEND_MEM)

其主要功能如下:

1) suspend_prepare: 准备进入suspend,并冻结所有进程

2) suspend_devices_and_enter: suspend所有外设,并进入sleep状态,只有当唤醒时,此函数才返回

3) suspend_finish: suspend结束,并被唤醒

enter_state代码如下:

[cpp] view plaincopy

// kernel/kernel/power/suspend.c

int enter_state(suspend_state_t state)

{

int error;

if (!valid_state(state))

return -ENODEV;

if (!mutex_trylock(&pm_mutex))

return -EBUSY;

#ifdef CONFIG_SUSPEND_SYNC_WORKQUEUE

suspend_sys_sync_queue();

#else

sys_sync();

printk("done.\n");

#endif

pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);

error = suspend_prepare(); //准备进入suspend,并冻结所有进程

if (error)

goto Unlock;

if (suspend_test(TEST_FREEZER))

goto Finish;

pr_debug("PM: Entering %s sleep\n", pm_states[state]);

pm_restrict_gfp_mask();

error = suspend_devices_and_enter(state); // suspend外部设备

pm_restore_gfp_mask();

Finish:

pr_debug("PM: Finishing wakeup.\n");

suspend_finish(); // 结束suspend,并被唤醒

Unlock:

mutex_unlock(&pm_mutex);

return error;

}


3.1 准备并冻结进程(suspend_prepare)

在suspend_prepare()中它将完成以下任务:

1) 给suspend分配一个虚拟终端来输出信息;

2) 然后广播一个系统要进入suspend的Notify;

3) 关闭掉用户态的helper进程;

4) 最后调用suspend_freeze_processes()冻结所有的进程,这里将保存所有进程 当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程。

其详细代码如下:

[cpp] view plaincopy

static int suspend_prepare(void)

{

int error;

if (!suspend_ops || !suspend_ops->enter)

return -EPERM;

pm_prepare_console(); // 分配一个console

error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); // 发送suspend notify

if (error)

goto Finish;

error = usermodehelper_disable(); // disable用户态的helper进程

if (error)

goto Finish;

error = suspend_freeze_processes(); // 冻结所有进程

if (!error)

return 0;

suspend_thaw_processes();

usermodehelper_enable();

Finish:

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

return error;

}


3.2 Suspend外部设备(suspend_devices_and_enter)

现在, 所有的进程(也包括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

[cpp] view plaincopy

int suspend_devices_and_enter(suspend_state_t state)

{

int error;

/* suspend_pos通过suspend_set_ops来进行注册,

它在kernel/arch/arm/mach-xx/pm.c中定义,其函数名

可能为xx_pm_ops,例子如下:

static struct platform_suspend_ops rk30_pm_ops = {

.enter = xx_pm_enter,

.valid = suspend_valid_only_mem,

.prepare = xx_pm_prepare,

.finish = xx_pm_finish,

};

*/

if (!suspend_ops)

return -ENOSYS;

trace_machine_suspend(state);

if (suspend_ops->begin) {

error = suspend_ops->begin(state);

if (error)

goto Close;

}

suspend_console(); // suspend console子系统,printk将不能打印了

suspend_test_start();

error = dpm_suspend_start(PMSG_SUSPEND); // suspend所有非系统设备

// 即执行所有设备的suspend回调函数

if (error) {

printk(KERN_ERR "PM: Some devices failed to suspend\n");

goto Recover_platform;

}

suspend_test_finish("suspend devices");

if (suspend_test(TEST_DEVICES))

goto Recover_platform;

error = suspend_enter(state); // 系统进入要求的sleep状态,

// 只有当wakeup时,此函数才返回

Resume_devices:

suspend_test_start();

dpm_resume_end(PMSG_RESUME); // resume所有非系统设备

// 即执行所有设备的resume回调函数

suspend_test_finish("resume devices");

resume_console(); // resume console子系统,即printk可用了

Close:

if (suspend_ops->end)

suspend_ops->end();

trace_machine_suspend(PWR_EVENT_EXIT);

return error;

Recover_platform:

if (suspend_ops->recover)

suspend_ops->recover();

goto Resume_devices;

}

3.2.1 suspend_console

Suspend console子系统,即printk将不能打印了

[cpp] view plaincopy

void suspend_console(void)

{

if (!console_suspend_enabled)

return;

printk("Suspending console(s) (use no_console_suspend to debug)\n");

console_lock();

console_suspended = 1;

up(&console_sem);

}

3.2.2 dpm_suspend_start (PMSG_SUSPEND)

Suspend所有非系统设备,即调用所有注册设备的suspend回调函数

[cpp] view plaincopy

/**

* dpm_suspend_start - Prepare devices for PM transition and suspend them.

* @state: PM transition of the system being carried out.

*

* Prepare all non-sysdev devices for system PM transition and execute "suspend"

* callbacks for them.

*/

int dpm_suspend_start(pm_message_t state)

{

int error;

error = dpm_prepare(state); // 根据dpm_list生成dpm_prepared_list

if (!error)

error = dpm_suspend(state); //根据dpm_prepared_list生成dpm_suspended_list

return error;

}

3.2.3 suspend_enter



使系统进入要求的sleep状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回,其详细代码如下:

[cpp] view plaincopy

/**

* suspend_enter - enter the desired system sleep state.

* @state: state to enter

*

* This function should be called after devices have been suspended.

*/

static int suspend_enter(suspend_state_t state)

{

int error;

if (suspend_ops->prepare) {

error = suspend_ops->prepare(); //即执行xx_pm_prepare

if (error)

goto Platform_finish;

}

error = dpm_suspend_noirq(PMSG_SUSPEND); //使所有外设驱动不再接收中断

if (error) {

printk(KERN_ERR "PM: Some devices failed to power down\n");

goto Platform_finish;

}

if (suspend_ops->prepare_late) {

error = suspend_ops->prepare_late();

if (error)

goto Platform_wake;

}

if (suspend_test(TEST_PLATFORM))

goto Platform_wake;

error = disable_nonboot_cpus(); // 停止非启动CPU

if (error || suspend_test(TEST_CPUS))

goto Enable_cpus;

arch_suspend_disable_irqs(); // 关闭中断

BUG_ON(!irqs_disabled());

error = syscore_suspend(); // 执行注册在syscore_ops_list的syscore_ops的suspend函数

if (!error) {

if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {

error = suspend_ops->enter(state); // KEY: 即执行xx_pm_enter,唤醒时才返回

events_check_enabled = false;

}

syscore_resume(); // 执行注册在syscore_ops_list的syscore_ops的resume函数

}

arch_suspend_enable_irqs(); // 打开中断

BUG_ON(irqs_disabled());

Enable_cpus:

enable_nonboot_cpus(); // 启动非启动CPU

Platform_wake:

if (suspend_ops->wake)

suspend_ops->wake();

dpm_resume_noirq(PMSG_RESUME); //使所有外设驱动接收中断

Platform_finish:

if (suspend_ops->finish)

suspend_ops->finish(); //即执行xx_pm_finish

return error;

}

3.2.4 dpm_resume_end (PMSG_RESUME)

resume所有非系统设备,即执行所有注册设备的resume回调函数

[cpp] view plaincopy

/**

* dpm_resume_end - Execute "resume" callbacks and complete system transition.

* @state: PM transition of the system being carried out.

*

* Execute "resume" callbacks for all devices and complete the PM transition of

* the system.

*/

void dpm_resume_end(pm_message_t state)

{

dpm_resume(state); //根据dpm_suspended_list生成dpm_prepared_list

dpm_complete(state); //根据dpm_prepared_list生成dpm_list

}

3.2.5 resume_console

resume console子系统,即printk可用了

[cpp] view plaincopy

void resume_console(void)

{

if (!console_suspend_enabled)

return;

down(&console_sem);

console_suspended = 0;

console_unlock();

}

3.3 Suspend结束(suspend_finish)

其主要功能如下(它是suspend_prepare的逆过程):

1) 解冻所有进程;

2) 打开用户态helper进程;

3) 广播系系统suspend结束的Notify;

4) 释放分配的虚拟终端。

其详细代码如下:

[cpp] view plaincopy

static void suspend_finish(void)

{

suspend_thaw_processes(); //解冻所有进程

usermodehelper_enable(); // 打开用户态helper进程

pm_notifier_call_chain(PM_POST_SUSPEND); // 广播系系统suspend结束的Notify

pm_restore_console(); // 释放分配的虚拟终端

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: