|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
Linux电源管理详解 " H% P4 E" Z2 t/ w: ?, h$ Y( H
" U3 t: y: y3 ]% c4 L- Z; `8 Q9 z+ N0 L) J3 i( l
1.概述
0 `1 | O- z0 L4 x' G" L& w- V& Z( E: Z: A! q# P! ^4 j7 N$ M l; ^2 g
虽然Linux可以在任何一台386以上的PC上运行,目前大多数人使用的都是新型的,带有各种外设的桌面PC或者笔记本电脑,这样,电源管理功能 (PM)就逐渐变得越来越重要。在笔记本电脑上电源管理可以节能,延长电池寿命,而在桌面PC上它可以降低幅射,降温,延长外设使用寿命。现在的操作系统大都内置了电源管理支持,例如 Windows 和 Linux。/ r! T: {% M- b! ?' V8 u
- q8 N$ S$ B$ d# l- L2.PC机实现电源管理的方法 8 }5 Y6 O, v/ ?6 h1 E$ T. a$ W
) u7 C. y% ]2 S( O1 O
要实现电源管理,最重要的有两点:第一是需要设备本身支持节电功能,比如硬盘,可以通过指令暂时关闭;第二是需要操作系统支持电源管理,这样就可以在空闲一段时间之后调用驱动的电源管理功能关闭设备。, ^1 E9 m; n+ D/ ~* L5 o
: h2 O5 b7 A+ j
两种电源管理标准:APM和ACPI
! a5 {! n9 s0 X) ~3 G4 u/ {$ E/ \! c
传统的APM(Advanced Power Management)是一种基于bios的电源管理标准,目前的最新版本是1.2,它提供了CPU和设备电源管理的功能,但是由于这种电源管理方式主要 是由bios实现,所以有些缺陷,比如对bios的过度依赖,新老bios之间的不兼容性,以及无法判断电源管理命令是由用户发起的还是由bios发起 的,对某些新硬件如USB和1394的不支持性。 为了弥补APM的缺陷,新的电源管理ACPI应运而生,这就是ACPI(Advanced Configuration and Power InteRFace),它主要是将电源管理的主要执行者由bios转换成为操作系统,这样可以提供更大的灵活性以及可扩展性。
; o( v2 h4 v$ q' S; z% T- B1 [" V, p) a; Z
目前的PC机主板一般同时支持APM和ACPI两种标准。 4 d$ F- r7 `& N( q1 y( J3 P9 t
1 I7 h* s/ T( y2 h2 ]+ D$ I3 M/ e3.Linux对电源管理的支持 R& o/ r0 h; Y: Q; ]2 d% S
* G R- d% `' C+ U7 P; D
内核模块 ( [" H& @& P+ J) B
; @: m8 I2 j+ a3 B 针对APM和ACPI两种不同的标准,Linux内核提供了两个不同的模块来实现电源管理功能,这就是apm和acpi。需要注意,apm和acpi是互相冲突的两个模块,用户在同一时间内只能加载其中之一,如果当他们在加载的时候发现二者之一已经加载,就会自动退出。
a4 p4 w( i4 b3 {% M0 ?! U6 z! T1 L) x. j4 c' H% X; |
在官方发布的内核中APM是较为成熟的电源管理方式,可以完成在Windows下ACPI所能完成的大部分功能。由于官方内核中ACPI的功能比较有限, 目前还处于开发版状态。所以当前的大多数distribution,如红帽子默认就使用了apm作为电源管理方式。但是值得注意的是Linux中的 ACPI实际上是由一个单独的项目小组模块进行维护的,当前内核ACPI的版本实际上已经远远落后于最新的版本。由于Linux稳定版中对任何新特性的加 入都非常谨慎小心,所以我们也许只能等到2.6.x版本的Linux诞生后才能看到ACPI的稳定全功能版了。不过我们也可以自己对内核打最新的ACPI 补丁来获得这些功能。
. i+ \1 \( [# f2 V& b% n/ B D7 g
这里是ACPI的主页:http://sf.net/projects/acpi/
& q7 [: C/ C& L9 }% p- `5 @! T' y) S% y$ s% x) O( @% r) `! h
下面对电源管理的介绍以APM为主。
: h, L7 G: m3 _# \% P) l3 D R. G& F X+ D# \8 D1 L
用户态Daemon
; M; _/ {! r4 o. ~5 d4 C6 O2 W
& f; L3 p5 G! ?# d) ^! t 为了让Linux内核中的电源管理功能够更好的被利用,我们还需要用户态daemon程序的配合。针对APM和ACPI,分别有apmd和acpid两个 不同软件。他们实现的功能比较类似,都是允许用户预先定义某些策略,然后跟踪电源状态,执行特定的操作。在apmd软件包中还有一个工具apm,用户可以 用它使机器主动进入standby和suspend状态,还可以查询bios的apm版本号。在使用acpi时直接对proc文件系统进行操作即可完成同 样的功能。 : i6 a# G# w; L! |7 c r
) x3 b' z. E7 b4.Linux下驱动的电源管理机制
% K& D, ^6 i7 j- r; B- a+ |2 l2 K
0 H3 S5 C5 o( n" P3 F$ P 在Linux下不必为驱动分别编写与APM和ACPI相对应的代码,Linux与Windows类似,为驱动提供了统一的电源管理接口。驱动只要实现了这些接口,就可以实现电源管理的功能。操作系统在它认为合适的时候就会通知驱动完成这些操作。# n& W7 h8 `8 G6 j2 u+ S
9 a) E6 b2 n, _6 e; U. h. c1 v
实现设备电源管理接口主要需要实现以下5点:
- g$ V7 y6 i: W. @. c+ Q5 p9 G. L# Y( V
1.使用pm_register对设备的每个实例(instance)进行注册; " L; T/ @2 O! s9 G7 y" a r
" p I% [) M3 @/ I4 J8 S
2.在对硬件进行操作之前调用pm_access(这样会保证设备已被唤醒并且处于ready状态);
+ u5 A3 X0 i+ S/ {$ g5 `
& g l- O/ k+ K! p 3.用户自己的pm_callback函数在系统进入suspend状态(ACPI D1-D3),或者从suspend状态恢复(ACPI D0)的时候会被调用;
' X; U% }( S, _2 j5 Y& B
/ G9 d5 |" m' k' a+ n 4.当设备不在被使用的时候调用pm_dev_idle函数,这个操作是可选的,可以增强设备idle状态的监测能力;
! H3 g1 K9 l8 @$ j% Y$ s# h; @3 P/ f; ^# E, Z8 A1 K/ m
5.当被unload的时候,使用pm_unregister来取消设备的注册。 * T7 Y: R/ F. U: B+ c" u
+ c5 @- U6 D y. D3 g5.对APM进行编程 " Z2 f& I2 ^6 F! j5 \
( M' B# {, l; T5 }3 ]. a" [ 下面介绍在实模式中和在Linux下使用APM功能的编程方法: 9 \% n' Q6 W' P) [
/ n8 @; ~+ D/ g) [: |) w9 [9 p7 G1 t; K
由于APM是由bios提供的,我们可以直接在实模式(如DOS下)调用int 15软中断来进行电源管理操作。 * D1 ~- x& [3 a
' ?8 I0 Y! x4 h4 _% ~ 在实模式下APM的standby、suspend和poweroff功能分别可以通过下面的汇编语言实现: ) n# x8 X- B, x9 O
1 T- G9 v/ d% `4 _ A8 r
standby:. C: S& t4 Z4 w4 k
mov ax, 5307H8 o1 ]! I" {* x; P
mov bx, 1" G* G% n) Z$ }) P) s& Q/ A+ l4 j
mov cx, 1: z6 @0 E. }6 ]+ V
int 15H6 o. Q' r' H; L2 w9 K) V6 q
" a3 a$ r+ }" A3 j6 `" H0 T) b
suspend:( `" I- j; c/ W; ~
改成 mov cx,2
% W$ O* P, I2 Z. E1 {( W* D4 B
1 P3 h. g0 z# X0 P" Spoweroff:6 G0 O* `7 Y5 Q9 T$ e8 `* K
改成 mov cx,3& _4 M$ Z3 y6 X& P, X5 M
0 L' Z' h# }+ P8 Q" Z7 X ?- [0 v9 {& W
' i! n% L) g8 a2 S7 J3 J/ x
需要注意的一件事是在Linux内核中没有使用和实模式的一样的方法来调用int 15H中断,而是直接调用了bios的保护模式接口。所以我们如果修改了bios中的apm相关代码并且没有处理好保护模式接口的问题,可以出现这样的情况:在实模式DOS下使用apm功能一切正常,但是在Linux下调用apm功能发生内核一般保护性错误。( v/ H& n, u! Q
8 m3 W" z& }7 }! P" `. ~
在Linux下我们可以通过对apm_bios设备的操作来完成同样的功能。 ' u9 t) y' j7 [, T& H2 e! g7 }
5 B! e' T* U: i# j X2 w$ G& `
下面的代码可以实现APM的suspend功能,等价于apm -s / Y' y: a0 ^( r& J& u! p; K X
- z# `* N) O% R# D! ^#include <stdio.h>
% y+ D5 h( o! V- P% A#include <unistd.h>& h4 x* P. D' o3 V: T2 c
#include <stdlib.h>4 ~: I: e; t4 ^4 t
#include <asm/fcntl.h>
) B' l/ c% [8 I: l% {#include <linux/types.h>
$ y6 {( a: R, ]) U' o#include <sys/types.h>
! S$ }: a9 A4 X9 G R% b. D#include "apm.h"
8 V$ }5 v/ v0 ^# ^, _0 g/ A% k0 T
% q% Y. m/ g% R3 E- J; y5 ~/ M& X/ vint main()' ?: {" j9 K& N$ b
{+ K6 E' }$ ^5 i; t$ O6 ?0 ?, }
int fd, res;
6 t4 s# p N* w. H8 H fd = open("/dev/apm_bios", O_RDWR);
: X( r7 U; o; Z8 A if (fd < 0) {
) {0 B8 X9 k& C8 L' R+ f. }: ^ printf("open /dev/apm_bios error!\n");' R8 a' i# A6 r
exit(-1);
1 v p* p1 s* k# Y0 K' k7 h }
% J3 }3 r: V" V! l( U sync();
2 n. ?. Z9 U# B* k/ f& f1 e+ H res = ioctl(fd, APM_IOC_SUSPEND, NULL);
0 Z6 P/ B9 e t7 {5 |8 |: s if (res != 0) {: A5 |, O K7 j" U
printf("ioctl error!\n");
. {. W# b* E h8 g5 Q/ z0 z6 z close(fd);
$ P. k/ J( }' e; z& c/ F exit(-1);
+ O+ H; v" g/ I% O" D$ l$ L }
R5 v- q& C/ `- H' m close(fd);9 w) U2 Q9 Q1 j* f% ^5 j
return 0;5 N6 d& T" r- F; x& U$ \1 F
}! u. ^* b# P6 k
; M( I- i+ {" o7 ~* e
( A/ f; N5 M$ |* x. ?. ]3 s& }$ d4 U) p& P6 W- B- s
如果我们把上面程序中的SUSPEND改成STANDBY,我们就同样实现了apm -S的功能。 8 U& u j0 O9 ^
, J: @* f& P2 p/ M* K% Q# ?
在Linux下POWEROFF操作有其独特的流程,最后根据内核中apm或者acpi的存在情况来执行相应不同的流程来关闭电源。请参见Linux内核源码,我写的《linux关机重启流程分析》中也有一定的介绍。8 M8 D4 x. g. `' L0 c' m
& @" i* E$ O; a' \+ R$ q4 X
0 E6 Q9 J! c1 c
6.常见问题(FAQ)
* ^1 K* O+ z+ T/ i: a! W! c2 v5 V2 [" o/ k* t
1)我的系统不能被suspend,这是怎么回事呢? ' h8 K; C: }8 P+ g
$ k6 R( }3 ?0 b
系统在suspend之前会向所有的驱动发消息,如果这个时候某个傲慢的驱动返回了一个-EBUSY,那么这次suspend的企图就被这个驱动否决了, 你只有过一会再试,如果这个驱动总是否决(真是蛮横,不过它也许有自己的苦衷也说不定),你就永远都无法suspend了。
B, g ^9 O6 i$ @8 t$ A4 D
' \4 U* U, F0 _( A x3 M$ J( ^7 T 2)我按下系统的POWEROFF开关,在ATX的主板上,系统就会自动关机了,这个处理流程是什么样子的呢?
* q- ^. I; I. ^8 S4 [( d
, B, p/ a" [4 U; T 在内核APM模块中建立了一个核心态线程不停的监测系统状态,用户的关机动作在这里被截获后处理。详细的流程可以参见本人的《linux关机重启流程分析》。 . [* S& @/ m9 A1 a
7 V9 b. P6 R( i3 @) f 3)Linux中电源管理的文档在哪里? / j$ @1 E, v4 d! Q( Z, {
; Z) [" q* I% y) z& ^3 _
在Linux/document.tion目录下的pm.txt中详细定义了Linux驱动电源管理接口实现方式,并且有详细的例子,apm和acpi的实现流程需要参见Linux源码的实现。
$ I9 r7 Z( @& J. e, u/ m* L+ ?* c: o# W+ G0 B
7.总结 ; s' z( w4 ?8 X" K
8 j9 f) p- b* b8 A Linux中的电源管理是发展中的代码。从目前的趋势来看ACPI终将取代APM。现在使用APM则是较为成熟和稳妥的方案。我们如果现在编写驱动应该严格遵守文档中的pm.txt所规定的接口,这样可以使我们的驱动有较强电源管理的适应性和稳定性。
( ?$ ?) V$ J, o0 m# B/ P |
|