3.1冻结用户进程进程冻结模块由CONFIG_FREEZER宏开关控制。
相关代码在kernel/power/process.c中,程序的结构比较简洁,总共就五个函数。
freezeprocess的控制过程都在freeze_processes函数中
[plain] view plaincopyprint?
- int freeze_processes(void)
- {
- int error;
-
- error = __usermodehelper_disable(UMH_FREEZING);
- if (error)
- return error;
-
- if (!pm_freezing)
- atomic_inc(&system_freezing_cnt);
-
- printk("Freezing user space processes ... ");
- pm_freezing = true;
- error = try_to_freeze_tasks(true);
- if (!error) {
- printk("done.");
- __usermodehelper_set_disable_depth(UMH_DISABLED);
- oom_killer_disable();
- }
- printk("\n");
- BUG_ON(in_atomic());
-
- if (error)
- thaw_processes();
- return error;
- }
进程的冻结控制过程如下:
1.冻结khelper内核线程。khelper是一个用于从内核空间调用用户空间应用程序的内核模块。能够在动态加载模块,热插拔等场景中发挥作用。
2.调用try_to_freeze_tasks冻结task_struct表中的任务。
3.如果try_to_freeze_tasks没有返回error则disableusermode helper 和 oom killer并成功返回。
4.如果try_to_freeze_tasks返回error则调用thaw_processes解冻操作。
try_to_freeze_tasks和thaw_process是一对反函数。冻结进程的工作是在两个循环中完成的。冻结进程要访问task_struct列表,所以先要加锁,然后用do_each_thread(g,p) 和while_each_thread(g,p)循环改变task_struct列表中进程的状态。把两个宏展开后的代码如下:
[cpp] view plaincopyprint?
- /×for循环从init_task(idle process)开始遍历所有进程×/
- for (g = p = &init_task ; (g = p = next_task(g)) != &init_task ; )
- do{
- /×如果是当前进程或者freeze_task失败则回到for循环开始下一个进程×/
- if (p == current || !freeze_task(p))
- continue;
- /×如果进程是stop或者应该跳过的进程则标记todo++,后面会专门针对这类进程重新处理×/
- if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p))
- todo++;
- }while ((p = next_thread(p)) != g) //while循环则从主线程开始遍历每个进程的所有线程
3.2挂起设备(Device Power Manager)
设备电源的管理涉及到两种不同的场景:一种是在系统电源状态发生转变的情况下,譬如从S0->S3或者S0->S4。另外一种是在系统处在S0,但是仍然有些不被使用的设备进入节电的状态,这种情况就是runtimedevice manager,对应在手持设备上,直接影响所谓的场景功耗。设备电源管理,同时涉及到器件,总线,控制器,子系统,同类设备的状态改变,对于操作的顺序上有一定的要求。举个例子,一条i2c总线上挂有camera,gensor,lighter sensor,那么i2c总线控制器必须要确保总线上的设备先进入lowpower或者off状态才能进入节电模式,因为Linux的驱动模型把总线和设备是分开控制的。另外许多设备都能作为系统的唤醒源,这个要求也要纳入统一的考虑。Linux2.5及以后的内核引入设备模型,把总线,设备,子设备,同一类型设备,子系统通过kobject组织起来,为devicepower manager提供了统一的控制平台。
通常情况下,设备进入suspend状态,通常都会I/O口停止工作,DAM停止工作,也不会请求中断,停止数据的读写传输。但是如果设备作为唤醒源,则依然为出发中断控制器的中断信号线。对于设备的管理,Linux在DPM(dynamicpowermanager)的架构下,提供了一整套机制,可以灵活的针对不同特性的设备。在include/linux/pm.h文件中定义了一组回调函数指针用于各种情况:
[cpp] view plaincopyprint?
- struct dev_pm_ops {
- int (*prepare)(struct device *dev);
- void (*complete)(struct device *dev);
- int (*suspend)(struct device *dev);
- int (*resume)(struct device *dev);
- int (*freeze)(struct device *dev);
- int (*thaw)(struct device *dev);
- int (*poweroff)(struct device *dev);
- int (*restore)(struct device *dev);
- int (*suspend_late)(struct device *dev);
- int (*resume_early)(struct device *dev);
- int (*freeze_late)(struct device *dev);
- int (*thaw_early)(struct device *dev);
- int (*poweroff_late)(struct device *dev);
- int (*restore_early)(struct device *dev);
- int (*suspend_noirq)(struct device *dev);
- int (*resume_noirq)(struct device *dev);
- int (*freeze_noirq)(struct device *dev);
- int (*thaw_noirq)(struct device *dev);
- int (*poweroff_noirq)(struct device *dev);
- int (*restore_noirq)(struct device *dev);
- int (*runtime_suspend)(struct device *dev);
- int (*runtime_resume)(struct device *dev);
- int (*runtime_idle)(struct device *dev);
- };
每个函数的应用场景都有专门的注释说明,详细的说明也在include/linux/pm.h文件中。针对设备电源管理的代码在driver/base/power/main.c文件中。电源管理相关的操作紧密的跟设备模型结合在一起。
device结构体中有两个与电源管理关系密切的成员:
[cpp] view plaincopyprint?
- struct dev_pm_info power;
- struct dev_pm_domain *pm_domain;
DPM则利用dev_pm_info结构体的power->entry成员和dev_pm_domain结构体的dev_pm_domain成员把参入系统电源管理的设备以及相关的操作串联起来的。下面是一个简单的数据流程:
|