标题:
linux电源管理更新
[打印本页]
作者:
look_w
时间:
2017-10-19 13:59
标题:
linux电源管理更新
3.1冻结用户进程
进程冻结模块由CONFIG_FREEZER宏开关控制。
相关代码在kernel/power/process.c中,程序的结构比较简洁,总共就五个函数。
freezeprocess的控制过程都在freeze_processes函数中
[plain]
view plain
copy
print
?
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 plain
copy
print
?
/×
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 plain
copy
print
?
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 plain
copy
print
?
struct
dev_pm_info power;
struct
dev_pm_domain *pm_domain;
DPM则利用dev_pm_info结构体的power->entry成员和dev_pm_domain结构体的dev_pm_domain成员把参入系统电源管理的设备以及相关的操作串联起来的。下面是一个简单的数据流程:
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0