Linux内核学习--电源管理
目录
一、引言
二、电源管理
------> 2.1、电源管理的两种模型
三、系统Suspend
------> 3.1、系统睡眠模型Suspend
------> 3.2、开启suspend
------> 3.3、驱动支持Suspend
四、runtime
------> 4.1、runtime_PM 框架
------> 4.2、使用Runtime功能
------> 4.3、驱动支持Suspend
------> 4.4、实例
------> 4.5、异步(ASYNC)和同步(SYNC)
一、引言
这篇文件简单的来介绍一下linux下的电源管理框架-PM
二、电源管理
1、电源管理的两种模型
以往接触的Linux驱动,没遇到使用电池供电的情况,因此几乎没关注电源的管理。
然而实际中,不少使用电池供电的硬件平台,例如手机、POS机等,就需要对电源进行管理,比如在不使用设备的时候,休眠屏幕省电。
Linux电源管理模型有两种:系统睡眠模型suspend和Runtime电源管理模型。
三、系统Suspend
1、系统睡眠模型Suspend
名称 | 含义 |
---|---|
On (on) | S0 - Working |
Standby (standby) | S1 - CPU and RAM are powered but not executed |
Suspend to RAM (mem) | S3 - RAM is powered and the running content is saved to RAM |
Suspend to Disk, Hibernation (disk) | S4 - All content is saved to Disk and power down |
其中
S3 aka STR(suspend to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。
S4 aka STD(suspend to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢。
系统休眠模型给我的感觉是以整机角度进行省电。
S3类似电脑的睡眠,在教长时间不使用电脑后,电脑黑屏,再次敲击键盘迅速显示桌面,原来的工作内容仍不变。
S4类似电脑的休眠,在长时间不使用电脑后,电脑黑屏,再次敲击键盘无反应,按下电源键,开机,原来的工作内容仍不变。
对于嵌入式设备,更多的是使用S3,将数据暂时放在内存里,以实现快速恢复,就像手机的电源键按下黑屏,再次按下迅速亮屏。
在Linux中,通过cat /sys/power/state可以得知当前设备支持的节能模式,一般情况有如下选项:
standby:前面的S1状态,CPU处于浅睡眠模式,主要针对CPU功耗;
mem:前面的S3状态,Suspend to RAM;
disk:前面的S4状态,Suspend to Disk;
需要设置以上模式,只需echo mem > /sys/power/state即可。下面来用我们的虚拟机试验一下
~$ cat /sys/power/state
freeze standby mem diskecho standby > /sys/power/state //进入待机
echo mem > /sys/power/state //进入待机
echo disk > /sys/power/state //进入休眠
睡眠可能有两种方式:mem和standby,这两种方式都是suspend to RAM,简称STR,只是standby耗电更多一些,返回到正常工作方式时间更短一些而已。
2、开启suspend
首先将suspend功能加入内核:
Power management options --->[*] Suspend to RAM and standby
设置唤醒源
要进入休眠,必须要有唤醒源,没有唤醒源,休眠也没有意义,唤醒源最常见的就是按键中断,就如同手机进入锁屏状态下,按下电源键唤醒一样
3、驱动支持Suspend
电源管理是在冻结APP之后和重启APP之前对驱动的电源进行控制
需要suspend和resume来实现
a.在platform_driver里的driver里添加pm结构体:
static struct platform_driver lcd_driver =
{.driver = {.name = "lcd_s702",.pm = &lcd_pm,.of_match_table = of_match_ptr(lcd_dt_ids),},.probe = lcd_probe,.remove = lcd_remove,
};
其中pm支持的操作接口有如下
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);
};
b.设置pm成员函数:
static struct dev_pm_ops lcd_pm = {.suspend = s702_lcd_suspend,.resume = s702_lcd_resume,
};
c.编写成员函数:
这个根据需求来编写了
随便来看一个内核中驱动的例子
static int gmux_suspend(struct device *dev)
{struct pnp_dev *pnp = to_pnp_dev(dev);struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);gmux_data->resume_client_id = gmux_active_client(gmux_data);gmux_disable_interrupts(gmux_data);return 0;
}
static int gmux_resume(struct device *dev)
{struct pnp_dev *pnp = to_pnp_dev(dev);struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);gmux_enable_interrupts(gmux_data);gmux_switchto(gmux_data->resume_client_id);if (gmux_data->power_state == VGA_SWITCHEROO_OFF)gmux_set_discrete_state(gmux_data, gmux_data->power_state);return 0;
}static const struct dev_pm_ops gmux_dev_pm_ops = {.suspend = gmux_suspend,.resume = gmux_resume,
};static struct pnp_driver gmux_pnp_driver = {.name = "apple-gmux",.probe = gmux_probe,.remove = gmux_remove,.id_table = gmux_device_ids,.driver = {.pm = &gmux_dev_pm_ops,},
};
可以看到,在suspend和resume中有开关中断等操作
四、runtime
1、runtime_PM 框架
前面的suspend系统睡眠模型是将整个系统进行休眠,但如果需要在系统运行时,单独对某个模块进行休眠 ,就需要Runtime电源管理模型,这两个模型互相协作,才能最大的发挥电源管理的效果。
Runtime电源管理模型的原理比较简单,就是计数,
当该设备驱动被使用时就加1,放弃使用时就减1,
计数大于1时,就打开该设备的电源,等于0时就关闭电源。调用我们注册的回调函数
Runtime PM相关的函数:
a. 使能/禁止 Runtime PM:pm_runtime_enable / pm_runtime_disable (修改disable_depth变量),这个变量的初始化值是1,默认是disable的状态
b. 增加计数/减少计数:pm_runtime_get_sync / pm_runtime_put_sync (修改usage_count变量),并判断是否进入suspend/resume
c. 回调函数 暂停/恢复/空闲:runtime_suspend / runtime_resume / runtime_idle
上面4个函数不会直接导致runtime_suspend,runtime_resume,runtime_idle被调用,只是使能和修改计数值,当引用计数减为0,调用suspend,从0变为大于0调用resume。
对于runtime PM,默认状态下设备的状态是suspend,如果硬件上它是运行状态,需要调用pm_runtime_set_active()来修改它的状态,然后调用pm_runtime_enable()来使能runtime PM。一般是在probe()的结尾处使用,以为它可能导致runtime的suspend/resume函数立即调用。一般在驱动remove中调用pm_runtime_disable()
在open()/release()接口中可以调用pm_runtime_get_sync/pm_runtime_put_sync
2、使用Runtime功能
调用方式一:
驱动程序提供接口, APP来调用。
在驱动函数的open()、close()里,增加和减少引用计数。
APP调用驱动的时候就能相应的恢复、暂停设备。
调用方式二:
直接操作应用层文件:
恢复:
echo on > /sys/devices/.../power/control
暂停:
echo auto > /sys/devices/.../power/control
3、使驱动支持Runtime
a.在platform_driver里的driver里添加pm结构体:(和前面的一样,这里就无需操作)
static struct platform_driver lcd_driver =
{.driver = {.name = "lcd_s702",.pm = &lcd_pm,.of_match_table = of_match_ptr(lcd_dt_ids),},.probe = lcd_probe,.remove = lcd_remove,
};
b.设置pm成员函数:
static struct dev_pm_ops lcd_pm =
{.suspend = s702_lcd_suspend,.resume = s702_lcd_resume,.runtime_suspend = s702_lcd_suspend,.runtime_resume = s702_lcd_resume,
};
c.编写成员函数:也是根据需求来
d.使能Runtime:
对于Runtime PM,默认状态下设备的状态是Suspended,
如果硬件上它是运行状态,需要调用pm_runtime_set_active()来修改它的状态,
然后调用pm_runtime_enable()来使能Runtime PM。
在probe()函数的后面添加:
pm_runtime_set_active(&pdev->dev);pm_runtime_enable(&pdev->dev);
反之,还要在remove()里禁止:
pm_runtime_disable(&pdev->dev);
e.修改计数:
一般在open()和release()里面增加和减少引用计数:
4、实例
1、定义
static const struct dev_pm_ops rtl8169_pm_ops = {.suspend = rtl8169_suspend,.resume = rtl8169_resume,.freeze = rtl8169_suspend,.thaw = rtl8169_resume,.poweroff = rtl8169_suspend,.restore = rtl8169_resume,.runtime_suspend = rtl8169_runtime_suspend,.runtime_resume = rtl8169_runtime_resume,.runtime_idle = rtl8169_runtime_idle,
};#define RTL8169_PM_OPS (&rtl8169_pm_ops)
static struct pci_driver rtl8169_pci_driver = {.name = MODULENAME,.id_table = rtl8169_pci_tbl,.probe = rtl_init_one,.remove = rtl_remove_one,.shutdown = rtl_shutdown,.driver.pm = RTL8169_PM_OPS,
};
可以看到,这个驱动注册了runtime机制,下面就来看一下这个计数是如何执行的
rtl_init_onertl_openpm_runtime_get_sync(&pdev->dev);pm_runtime_put_noidle(&pdev->dev); //only put,只减少计数器
rtl8169_closepm_runtime_get_sync(&pdev->dev); //增加计数...... //释放内存的去初始化工作pm_runtime_put_sync(&pdev->dev);
rtl_shutdownpm_runtime_get_sync(d); pm_runtime_put_noidle(d);
rtl_remove_onepm_runtime_get_noresume(&pdev->dev);pm_runtime_put_noidle(&pdev->dev);
5、异步(ASYNC)和同步(SYNC)
设备驱动代码可在进程和中断两种上下文执行,因此put和get等接口,要么是由用户进程调用,要么是由中断处理函数调用。由于这些接口可能会执行device的.runtime_xxx回调函数,而这些接口的执行时间是不确定的,有些可能还会睡眠等待。这对用户进程或者中断处理函数来说,是不能接受的。
因此,RPM core提供的默认接口(pm_runtime_get/pm_runtime_put等),采用异步调用的方式(由ASYNC flag表示),启动一个work queue,在单独的线程中,调用.runtime_xxx回调函数,这可以保证设备驱动之外的其它模块正常运行。
另外,如果设备驱动清楚地知道自己要做什么,也可以使用同步接口(pm_runtime_get_sync/pm_runtime_put_sync等),它们会直接调用.runtime_xxx回调函数,不过,后果自负!
Linux内核学习--电源管理相关推荐
- Linux内核学习--内存管理模块
Linux内核学习--内存管理模块 首先,Linux内核主要由五个部分组成,他们分别是:进程调度模块.内存管理模块.文件系统模块.进程间通信模块和网络接口模块. 本部分所讲的内存是内存管理模块,其主要 ...
- Linux内核配置电源管理
最近测试板子辐射比较高,希望能在运行时降低功耗和辐射,CPU这部分没啥用到,可以降! [*] Power Management support //如果你想让你的Linux支持高级电源管理(也就是平常 ...
- Linux内核学习008——进程管理(四)
Linux内核学习007--进程管理(四) 进程家族树 Unix系统的进程之间存在一个明显的继承关系,所有的进程都是PID为1的init进程的后代.内核在系统启动的最后阶段启动init进程,然后ini ...
- 关闭linux服务器电源,linux关闭ACPI电源管理模块
一.运行环境 # cat /etc/redhat-release CentOS release 6.2 (Final) # uname -a Linux web-server- 2.6.-.el6.x ...
- linux内核之内存管理.doc,linux内核之内存管理.doc
Linux内核之内存管理 作者:harvey wang 邮箱:harvey.perfect@ 新浪博客地址:/harveyperfect ,有关于减肥和学习英语相关的博文,欢迎交流 把linux内存管 ...
- 我的Linux内核学习笔记
在开始今天的内容之前,其实有一些题外话可以和大家分享一下.自从工作以来,我个人一直都有一个观点.那就是怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问.有些道理看上去 ...
- Linux内核学习编译流程
一.前言 linux内核学习 1.安装vmware虚拟机或者virtualbox,再安装发行版本linux 2.www.kernel.org,挑选一个内核版本 3.进行解压并编译 4.自己写一些模块( ...
- Linux内核学习路线
[推荐阅读] 手把手教你如何编写一个Makefile文件 一文讲解,Linux内核--内存管理(建议收藏) 当Linux内存耗尽时,改如何处理! 一文看懂页面置换算法 内核学习路线 很多同学接触Lin ...
- Android内核驱动——电源管理
Android内核驱动--电源管理 13.1 基本原理 Android 中定义了几种低功耗状态:earlysuspend,suspend,hibernation. earlysuspend是一种低 ...
最新文章
- 保存数组_面试官:讲一讲你对据结构——数组、链表、栈、队列的理解
- 手把手实现火爆全网的视频特效 “蚂蚁呀嘿”,太魔性了
- JAV A获取项目路径
- 每天一个linux命令(19):find 命令概览
- html公共模块提取出去,webpack 填坑之路--提取独立文件(模块)
- 新版刷卡_有信用卡的注意了,新版征信即将上线,以后刷卡消费要当心了!
- redhat7.1安装mysql_redhat7.1 安装mysql 5.7.10步骤详解(图文详解)
- Sahi ---实现 Web 自动化测试
- codeforces731E Funny Game(DP)
- 724. 寻找数组的中心索引
- [CI、CD入门]maven打包可执行程序之微服务-服务提供者篇
- 微信小程序轮播图,图片自适应,图片循环播放,图片之间有空白空间
- 信息安全必备的8张思维导图
- CATIA二次开发—参数那点事
- 形如e^(ax^2+bx+c)的积分公式的证明
- 【计导非课系列】 第五节 二进制 进制计算 编码
- 值得一看的网络课程推荐(不限于计算机科学)
- jxls2-java生成/导出excel工具!基于jxls2写的jxls增强版jxlss的完整教程
- 深入理解debuginfo
- 软件需求工程 高校教学平台 用户手册