|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
Linux电源管理详解
2 n3 b$ p0 x& I" z8 B& y8 Z( \3 R: J/ W1 J0 k; R- z: P" N5 J
( Z. D7 N; p# y d7 e
1.概述 6 |- y# e. H& B( v3 V4 G6 @
2 i0 h* n# t1 R9 t1 q) o
虽然Linux可以在任何一台386以上的PC上运行,目前大多数人使用的都是新型的,带有各种外设的桌面PC或者笔记本电脑,这样,电源管理功能 (PM)就逐渐变得越来越重要。在笔记本电脑上电源管理可以节能,延长电池寿命,而在桌面PC上它可以降低幅射,降温,延长外设使用寿命。现在的操作系统大都内置了电源管理支持,例如 Windows 和 Linux。
1 ^; \4 N) V2 x8 q; M
- z Q9 ^+ i" D& `0 M- S2.PC机实现电源管理的方法
9 |& _! U# @! B$ r. e
: E9 x: x5 f+ _* V9 C0 V 要实现电源管理,最重要的有两点:第一是需要设备本身支持节电功能,比如硬盘,可以通过指令暂时关闭;第二是需要操作系统支持电源管理,这样就可以在空闲一段时间之后调用驱动的电源管理功能关闭设备。
! P* q& A! Z0 [) _2 b! ]# K2 T3 c
- { O- \! v9 j1 w两种电源管理标准:APM和ACPI
: n( U+ {( G& o; U) B6 b8 F& U9 G5 L4 F5 a+ B8 c) j
传统的APM(Advanced Power Management)是一种基于bios的电源管理标准,目前的最新版本是1.2,它提供了CPU和设备电源管理的功能,但是由于这种电源管理方式主要 是由bios实现,所以有些缺陷,比如对bios的过度依赖,新老bios之间的不兼容性,以及无法判断电源管理命令是由用户发起的还是由bios发起 的,对某些新硬件如USB和1394的不支持性。 为了弥补APM的缺陷,新的电源管理ACPI应运而生,这就是ACPI(Advanced Configuration and Power InteRFace),它主要是将电源管理的主要执行者由bios转换成为操作系统,这样可以提供更大的灵活性以及可扩展性。
7 z2 T; n% K3 z1 `
+ h; K: E( q' ~1 d2 a2 f 目前的PC机主板一般同时支持APM和ACPI两种标准。 ( L6 Y2 @: ]( F
* _! s. p0 E( r6 J3 x8 Y3.Linux对电源管理的支持 ! d$ F( Q$ {! u3 s
1 y% i" b& e# _内核模块
) W5 K9 ^: z7 c1 I) V
' ~! C5 A& z9 ~8 u) V9 h$ T 针对APM和ACPI两种不同的标准,Linux内核提供了两个不同的模块来实现电源管理功能,这就是apm和acpi。需要注意,apm和acpi是互相冲突的两个模块,用户在同一时间内只能加载其中之一,如果当他们在加载的时候发现二者之一已经加载,就会自动退出。4 M( Y" E+ x& f8 }# W( t7 J# T s
( F; }( Z2 g) I e2 j: p$ v 在官方发布的内核中APM是较为成熟的电源管理方式,可以完成在Windows下ACPI所能完成的大部分功能。由于官方内核中ACPI的功能比较有限, 目前还处于开发版状态。所以当前的大多数distribution,如红帽子默认就使用了apm作为电源管理方式。但是值得注意的是Linux中的 ACPI实际上是由一个单独的项目小组模块进行维护的,当前内核ACPI的版本实际上已经远远落后于最新的版本。由于Linux稳定版中对任何新特性的加 入都非常谨慎小心,所以我们也许只能等到2.6.x版本的Linux诞生后才能看到ACPI的稳定全功能版了。不过我们也可以自己对内核打最新的ACPI 补丁来获得这些功能。
( C' A5 r& w4 ?- }7 T7 R" W& C. ]4 C* P( K* b, n) w& Z# ?! E9 ~
这里是ACPI的主页:http://sf.net/projects/acpi/$ p/ O6 e8 I3 A) Y$ D
! G3 a0 v$ V5 C1 ]( B4 ` 下面对电源管理的介绍以APM为主。 % V$ J4 U3 E$ q7 [2 z
8 L# a: R5 F! i
用户态Daemon 3 v7 t$ a' h7 J
5 }% Q) v4 Q0 G+ G5 G+ z E7 h7 B 为了让Linux内核中的电源管理功能够更好的被利用,我们还需要用户态daemon程序的配合。针对APM和ACPI,分别有apmd和acpid两个 不同软件。他们实现的功能比较类似,都是允许用户预先定义某些策略,然后跟踪电源状态,执行特定的操作。在apmd软件包中还有一个工具apm,用户可以 用它使机器主动进入standby和suspend状态,还可以查询bios的apm版本号。在使用acpi时直接对proc文件系统进行操作即可完成同 样的功能。
3 z. p4 U, H: o7 t0 x( h% M1 n& l s/ E0 G$ e f
4.Linux下驱动的电源管理机制 2 k: w- z! O% W H" f. N5 G' C
c6 W+ A0 w5 Z
在Linux下不必为驱动分别编写与APM和ACPI相对应的代码,Linux与Windows类似,为驱动提供了统一的电源管理接口。驱动只要实现了这些接口,就可以实现电源管理的功能。操作系统在它认为合适的时候就会通知驱动完成这些操作。
0 V8 U# V. i, S6 \( j+ R8 d& J
' z: q7 D, a6 `! ?5 P 实现设备电源管理接口主要需要实现以下5点: ) k5 ~, Y2 Y, \# @
1 c; I& T& c4 n0 ] 1.使用pm_register对设备的每个实例(instance)进行注册;
) }' d) D# k) A/ W$ V. M' D' x
& S9 d o8 W% g( M7 x, ~$ y 2.在对硬件进行操作之前调用pm_access(这样会保证设备已被唤醒并且处于ready状态);
/ o/ n2 H& P3 q! }' k3 D( a. |# U) a# t$ |
3.用户自己的pm_callback函数在系统进入suspend状态(ACPI D1-D3),或者从suspend状态恢复(ACPI D0)的时候会被调用; $ a6 ~1 n. M+ K* A
4 A) H" t% h* v$ x- F3 O
4.当设备不在被使用的时候调用pm_dev_idle函数,这个操作是可选的,可以增强设备idle状态的监测能力;
6 _6 U9 A! D5 `8 Q' u& P/ p$ v' @$ m1 [) g' I( Z
5.当被unload的时候,使用pm_unregister来取消设备的注册。 ; R/ G4 G8 u; F5 P3 Z& Z% k, y* E4 Z7 K
& X3 c" B5 S* x. t8 u
5.对APM进行编程 % h4 Z7 g# C$ O& t
' r S3 [+ p$ V 下面介绍在实模式中和在Linux下使用APM功能的编程方法: 9 p9 G' H! P1 T# n
8 q, F2 z% `. Q# k: J1 v7 R
由于APM是由bios提供的,我们可以直接在实模式(如DOS下)调用int 15软中断来进行电源管理操作。 " u! ]: k, b& ~# ?* {
6 W' ?/ P& Q" \2 w0 |4 K5 r
在实模式下APM的standby、suspend和poweroff功能分别可以通过下面的汇编语言实现:
8 A' F/ i* r% u+ c' Z$ E- m- ^' x: d h
standby:6 ^) e) T. l+ R- ]9 N
mov ax, 5307H
+ o( a" u' |" E; D! Y1 Fmov bx, 11 l4 [6 g: v3 x6 F
mov cx, 1
/ |) e: E3 B7 j6 T0 l7 Qint 15H
i, R; l4 t' }. `6 \8 ? x; d. ^2 L: ?& R4 c
suspend:+ s4 G) l& e& ^ I% `7 R5 L# B$ ?2 c
改成 mov cx,22 x3 t& q( `7 \' E
4 h' w# B/ ^* ]2 [% p( o; f" J. {
poweroff:6 j9 E/ \6 w* l
改成 mov cx,3
( p" _$ @1 u# g. z' J: A- j7 G/ m9 y' Z2 [1 s6 X4 f: b' ]
* I) v" \. G1 g7 \/ x
0 Z! d. a" P3 ]1 {) A5 i 需要注意的一件事是在Linux内核中没有使用和实模式的一样的方法来调用int 15H中断,而是直接调用了bios的保护模式接口。所以我们如果修改了bios中的apm相关代码并且没有处理好保护模式接口的问题,可以出现这样的情况:在实模式DOS下使用apm功能一切正常,但是在Linux下调用apm功能发生内核一般保护性错误。
0 c% v( R/ r+ b' f: [$ t! u
: j6 v# [, \3 ?) t) s% Q' W 在Linux下我们可以通过对apm_bios设备的操作来完成同样的功能。 , ? T; X+ y+ B6 q# n% y
6 x$ L5 y8 M" W$ C5 T- z4 l3 l+ {
下面的代码可以实现APM的suspend功能,等价于apm -s
0 o! ?2 e9 G$ T+ O( x2 {) j0 Z. a& ?# E4 V q! r9 ~+ i7 {) S: G
#include <stdio.h>
`4 V4 r/ w. a. R p* k) H#include <unistd.h>9 m. N" u0 O) T& X5 h
#include <stdlib.h>& s! f! C8 n' m' r) h
#include <asm/fcntl.h>
- M6 E1 M" A' q#include <linux/types.h>! t( e$ @% X" B# {7 n' T0 e
#include <sys/types.h>
% M8 G( G/ U2 a2 N, l#include "apm.h"
5 u8 M1 ~* I! N1 l; K; _3 {/ H( b, j% Y8 Q6 n
int main()+ Q0 z0 }& h0 o2 I6 R1 Y) G
{
* A, V% F, ?6 D! D; q$ `+ E7 \% i int fd, res;
9 s; R3 P8 I/ s! B- s fd = open("/dev/apm_bios", O_RDWR);* P9 s+ l: j. d/ Z
if (fd < 0) {
5 |, O7 W3 R$ G printf("open /dev/apm_bios error!\n");+ n4 H& x+ R( O1 D0 u: f5 o
exit(-1);
4 X; u# c0 R& ~' l( c; ] }* R' M- _! v2 ~# j+ r
sync(); f/ H9 G' h# P9 j/ ~
res = ioctl(fd, APM_IOC_SUSPEND, NULL);) X" R/ Y9 H# e3 y* g8 {% {
if (res != 0) {6 V# G9 A& T8 R: P- n
printf("ioctl error!\n");
& r# J1 ~2 c2 j c0 y# Q. F- R close(fd);; e# z; Y# N1 @1 b% s5 p+ y
exit(-1);9 h$ }/ c; C4 c; R
}& [6 R% O5 A1 n P
close(fd);7 G+ J" p2 G5 ~9 ?, b+ B2 O
return 0;+ j: B* U) ~* E8 _/ d, F+ s
}
, C: H' e1 J% ]. W8 B! p
( T5 z% P$ L( n- M4 e/ D1 U' D3 c, X- a/ {
/ Q6 ]* p2 c- r" a# U7 r( l5 y4 o
如果我们把上面程序中的SUSPEND改成STANDBY,我们就同样实现了apm -S的功能。 1 @4 ]) e" O. [$ W4 n7 z
& Q6 f4 |/ }, V+ r) F 在Linux下POWEROFF操作有其独特的流程,最后根据内核中apm或者acpi的存在情况来执行相应不同的流程来关闭电源。请参见Linux内核源码,我写的《linux关机重启流程分析》中也有一定的介绍。
* Z3 h5 |( ~$ f- \( S7 ^1 u" [( K$ ^2 h* }1 |9 j/ Z- E
% f$ [& G$ G+ a0 A" W6.常见问题(FAQ)
- r! Z+ H4 g: E- S1 U8 m& M* [. j
0 c& o) u' d3 ~ 1)我的系统不能被suspend,这是怎么回事呢?
+ c; K! N% z" l
8 d! Q0 G; B* f! R, ?5 u ? 系统在suspend之前会向所有的驱动发消息,如果这个时候某个傲慢的驱动返回了一个-EBUSY,那么这次suspend的企图就被这个驱动否决了, 你只有过一会再试,如果这个驱动总是否决(真是蛮横,不过它也许有自己的苦衷也说不定),你就永远都无法suspend了。3 `& }! v! |3 ^+ p3 Z0 [; v( n
& I: o7 t$ C% R* ?! K! L9 f' [, U 2)我按下系统的POWEROFF开关,在ATX的主板上,系统就会自动关机了,这个处理流程是什么样子的呢? , |& T* D, @- @4 y5 c6 ~$ D7 T
! n1 f+ J- }$ _' H7 h3 Y q" m
在内核APM模块中建立了一个核心态线程不停的监测系统状态,用户的关机动作在这里被截获后处理。详细的流程可以参见本人的《linux关机重启流程分析》。 0 s+ w$ w; ^5 `; j. x% q: t& [ S( V
7 s: n- y) z9 s" i& R. I
3)Linux中电源管理的文档在哪里? ' N( \0 A% M k8 o2 ]2 o+ W
8 o% h6 a9 a0 Q7 H 在Linux/document.tion目录下的pm.txt中详细定义了Linux驱动电源管理接口实现方式,并且有详细的例子,apm和acpi的实现流程需要参见Linux源码的实现。/ n, N( B# e$ z% N) ]( i& J
. P( {) A. F+ |
7.总结
2 W8 g: N# [% {. h" n$ M7 p: I6 J
' G/ A( D0 [& J Linux中的电源管理是发展中的代码。从目前的趋势来看ACPI终将取代APM。现在使用APM则是较为成熟和稳妥的方案。我们如果现在编写驱动应该严格遵守文档中的pm.txt所规定的接口,这样可以使我们的驱动有较强电源管理的适应性和稳定性。6 M5 {5 G0 i+ b0 P5 g
|
|