linux下的关机和重启流程对于一般的桌面应用和
网络服务器来说并不重要,但是在用户自己定义的嵌入式
系统内核中就有一定的研究意义,通过了解
Linux关机重启的流程,我们对它可以修改和自定义,甚至以此为基础开发出全新的
功能来。
U' K! ^2 |$ k# U1.概述
8 t7 J0 ?& g* `9 j1 K- f
* t# k+ r8 d, V O* l% S( W
在linux下的关机和重启可能由两种行为引发,一是通过用户
编程,一是系统自己产生的消息。用户和系统进行交互的方式也有两个,一个是系统调用:sys_reboot,另一个就是apm或则acpi的设备文件,通过对其操作也可以使系统关机或者重启。
- k' t3 ]3 S, p3 p' F ]% P
, q" A' I6 [) e2.通过系统调用sys_reboot的重启
! g+ T7 S" B7 @# i8 y) E9 w
- j4 ~+ S z' U& c% H+ x$ d
这个系统调用定义了一系列的MAGIC_NUMBER,在调用的开始部分首先检查MAGIC_NUMBER是否正确,只有正确才继续向下运行。在重启的时候转向分支
9 \+ C4 A- F5 K5 l3 z$ g
0 T% F- r% z- _) l* ^& x* R
caseLINUX_REBOOT_CMD_RESTART:
7 h9 ^8 j# T$ }% v3 n
0 A, a) R/ v4 W. C' V- m) j' |
首先使用notifier_call_chain向其它部分发出重启的消息,然后调用machine_restart函数完成重启。
' S- e$ T# `. J9 X4 @# Q
/ v1 a- c" ]0 ?$ Z* H
machine_restart函数的开始部分有一段SMP相关的代码,主要完成多
CPU时由一个
CPU完成重启操作,其它
CPU处于等待状态。之后系统根据一个变量reboot_thru_bios的内容判断重启方式,通过阅读reboot_setup我们可以得知,这个参数的内容是在系统启动时指定的,决定了是否利用bios,事实上是系统复位后的入口(FFFF:0000)地址的
程序进行重启。在不通过bios进行重启的情况下,系统首先设定了重启标志,然后向
端口0xfe写入数字0x64,这种重启的具体原理我还不大清楚,似乎是模拟了一次reset键的按下,希望大家和我讨论。在通过bios重启的情况下,系统同样先设定了重启模式,然后切换到了实模式,通过一条ljmp$0xffff,$0x0完成了重启。
) C9 ~" Z7 x$ o' l0 E[color=#FFFFFF']
6 X' j0 j: T8 d; ?1 L9 c0 k8 y
. F3 m4 w: j' Z7 u9 P% [4 `) O# g
+ O7 S* I4 o2 s/ L3.通过系统调用sys_reboot进行关机
! j- l7 p& r1 `7 p
2 K( h! s9 U# S- ~+ w0 y在系统调用的处理分支上,我们可以看到,首先同样是检查MAGIC_NUMBER,然后在
* S! p1 ?% u1 N2 o: Z' ]
) q0 O- `/ J9 H
caseLINUX_REBOOT_CMD_POWER_OFF:
* m4 x) }8 Y/ H) d: Z9 f% \- f
的执行流程里面,又是使用notifier_call_chain发出了关闭
计算机电源的消息,紧接着执行了machine_power_off函数。我们在machine_power_off函数中可以看到,如果pm_power_off这个函数指针不为空,那么系统就会通过调用这个函数进行关机。在apm已经加载的情况下(SMP除外),实际上pm_power_off函数实际上指向了apm.c中的apm_power_off,在这个函数里系统通过apm_info
结构里的值,使用切换到实模式关机,或者使用apm_bios_call_simple函数调用保护模式下的apm接口关机两种方法。
, X; i0 q7 |& ^) v3 B! L) E6 A% |9 ?
4.apm
驱动本身的关机过程
" M3 Z1 I# i0 S
) H+ ]/ k* q' hapm使用其注册的设备的ioctl接口完成apm的操作,在apm.c的do_ioctl函数中可以看见处理的分支。这里只有suspend和standby的代码,所以我们不能通过ioctl这种方法使用apm关机。
[ u' ~9 E$ ]$ h& Y6 r% b% O2 k# `7 X3 [# C/ k( r- \8 x. [
当用户按下POWER开关的时候,如果有apm模块,那么关机流程是由apm来处理的。apm驱动在初始化的时候启动了一个apm内核线程:apm_mainloop,系统会在这里检测到POWEROFF按键消息并且将其命名为APM_SYS_SUSPEND,以区别apm-s设置的APM_USER_SUSPEND模式。紧接着进入了apm_event_handler函数,又从apm_event_handler函数进入了check_events函数,处理函数对应的case分支上。系统同样使用了suspend函数进行关机,不过由于其它参数的原因,suspend最后调用的是关机的流程。
3 T' W. h! Z: s( v9 i& M
2 K+ f" L6 t% Q: P/ E: @/ F0 ?
5.解决问题实例
4 z( S9 k+ G8 w0 E# G4 R- j
6 L, h3 k' e# Z7 x4 t) _1)按POWER键时某些主板死机
7 Y) `+ w* g8 l/ K0 ]' O
- G" i2 s' ]. v" s
经查只有某些特定的驱动装载之后才会出现这样的情况,并且当使用关机系统调用sys_reboot的时候没有这样的问题。分析apm的处理流程,怀疑是在关机前驱动程序没有正确处理apm发出的询问消息造成的。由于部分驱动程序没有
源代码,决定hack掉apm.c的关机部分,让两种方式的关机走同样的流程。于是把apm.c的check_events函数中对APM_SYS_SUSPEND部分改写为如下代码:
. b9 ~: e# f' U: ] i- G( M
+ d8 ]2 r# O" [; M3 h
. R4 F* A& c$ w
ret=exec_usermodehelper(poweroff_helper_path,argv,envp);
5 C" U7 A+ W2 ]8 O& k
if(ret){
f$ h* J, E4 d. j$ @; `9 R) ^printk(KERN_ERR
( _! h8 W% x' S; Q! M
"apm.c:failedtoexec%s,errno=%d\\n",
) V3 q! I! _1 n' I' @poweroff_helper_path,errno);
4 B0 }* P5 P; F+ t( r' q# b}
, X$ n* ^- P; ^5 i; d; vbreak;
" P; H$ T0 H, b, G# T
@& X8 C! G# L6 [2 Z6 W
Forfastrebootsupport
- [ E2 \9 r2 l, }$ ostaticunsignedcharfast_reboot_switch[]=
. j2 Z( `/ {- }{
& ~ K" R9 ~! A1 f/ N8 V6 }4 b9 ]% V0x66,0x0f,0x20,0xc0,/*movl%cr0,陎*/
( w. H8 k9 S7 h$ F9 K+ j0x66,0x25,0x10,0x11,0x11,0x11,/*andl$0x11111110,陎*/
4 H6 B- K& p, I: e2 p0x66,0x0f,0x22,0xc0,/*movl陎,%cr0*/
! K/ }' q3 h' r( x/ X, n6 @+ k* u: m0xea,0x00,0x00,0x00,0x70/*ljmp$0x7000,$0x0000*/
r; ]# `( D& E l( \+ j# J
1 W$ T$ G( Z2 B7 H- g0 {
};
8 A4 x) N+ X+ c* t
) H( g& u& N r+ [2 t h- x7 G" V# H7 b, h! ^6 U, w
X1 y: P" q" k) [& \6 F9 J
" e3 Y7 }* n- G& m% p* c2 |0 P# U
i9 \5 R% P* w系统就可以切换到实模式中,然后跳转到7000H:0位置开始执行。
) o7 e; I/ ?, e
( n6 |9 B }3 k Z- |; D' A6.ACPI概述
2 t( I) E- {6 |! y5 F
3 ?- N8 D" K+ S0 D; b, `" _在2.4.20内核中ACPI模块被注明为试验和未完成,里面有一部分功能也许没有实现。如果APM和APCI两个模块同时编译进内核,APM在ACPI前被加载,APM起作用使ACPI退出。对于系统电量、电源实践一
类的支持(主要是在
笔记本上有用),靠的是acpid这个daemon程序。
$ a% _3 C2 n4 N: T6 @# g5 U/ j/ v3 E5 `& E/ m& G; z
没有一个功能类似apm的应用程序切换状态,acpi的程序仅仅完成了对acpi状态的查询。用户实现S0-S4的功能可以直接向/proc/acpi/sleep文件中写入数字来实现。通过读出(cat)其中的内容可以知道系统到底支持那些模式。
0 _1 X1 ?6 y5 G3 T1 C5 U
4 w& G( N* q* c, tacpi模块的源代码主程序在linux/drivers/acpi/driver.c中,如果向sleep文件写东西,就转到了linux/drivers/acpi/ospm/system/sm_osl.c文件的sm_osl_proc_write_sleep函数中,这个函数后来调用了sm_osl_suspend函数。在这个函数里完成了各种功能,包括保护各种状态。最后真正的sleep是通过对acpi_enter_sleep_state的调用完成的,这个函数在linux/drivers/acpi/hardware/hwsleep.c文件中,这里写了acpi的寄存器使系统进入sleep状态。写寄存器的指令在这个目录下面的hwregs.c中。
) a- i& `; H) g$ K$ W, r, |
7 B: T8 R' y" J+ z7.总结
1 I7 O# K- U2 w1 X' K7 h, [# j+ w- D; W( ]5 X7 Y5 i
本文对acpi的介绍非常简略,实际上ACPI必定会成为将来linux内核中首选的电源
管理方式。由于目前官方代码中ACPI版本较低,所以没有太详细的论述,希望将来的内
核能有所改变。
' P: h( L8 l% S! R' \2 ]& S9 g7 T9 }7 x8 T: K) v0 K
参考资料
/ [! E$ D# @& c8 i1 z# D
( ?7 [5 }5 t1 v3 @. qlinux-2.4.20源代码
! W; Z3 B- s0 ]
; v1 S, q% a- o1 Z% q& o& `* C, K/ N2 g$ P( t6 R
关于作者
' @8 C( U: b/ Z" o
6 L3 Y) @) J) f' o( E* _8 t% s8 H
范晓炬,
联想(北京)有限公司
软件设计中心嵌入式研发处开发工程师,研究兴趣为Linux内核,网络
安全,XWindow系统,Linux桌面应用,
人工智能系统。你可以通过xiaoju_f@263.net联系他。