Linux 關機重啟流程分析(轉)

post0發表於2007-08-09
Linux 關機重啟流程分析(轉)[@more@]

linux下的關機和重啟流程對於一般的桌面應用和網路伺服器來說並不重要,但是在使用者自己定義的嵌入式系統核心中就有一定的研究意義,透過了解Linux 關機重啟的流程,我們對它可以修改和自定義,甚至以此為基礎開發出全新的功能來。

1.概述

在linux下的關機和重啟可能由兩種行為引發,一是透過使用者程式設計,一是系統自己產生的訊息。使用者和系統進行互動的方式也有兩個,一個是系統呼叫:sys_reboot,另一個就是apm或則acpi的裝置檔案,透過對其操作也可以使系統關機或者重啟。

2.透過系統呼叫sys_reboot的重啟

這個系統呼叫定義了一系列的MAGIC_NUMBER,在呼叫的開始部分首先檢查MAGIC_NUMBER是否正確,只有正確才繼續向下執行。在重啟的時候轉向分支

case LINUX_REBOOT_CMD_RESTART:

首先使用notifier_call_chain向其它部分發出重啟的訊息,然後呼叫machine_restart函式完成重啟。

machine_restart函式的開始部分有一段SMP相關的程式碼,主要完成多CPU時由一個CPU完成重啟操作,其它CPU處於等待狀態。之後系統根據一個變數reboot_thru_bios的內容判斷重啟方式,透過閱讀reboot_setup我們可以得知,這個引數的內容是在系統啟動時指定的,決定了是否利用bios,事實上是系統復位後的入口(FFFF:0000)地址的程式進行重啟。在不透過bios進行重啟的情況下,系統首先設定了重啟標誌,然後向埠0xfe寫入數字0x64,這種重啟的具體原理我還不大清楚,似乎是模擬了一次reset鍵的按下,希望大家和我討論。在透過 bios重啟的情況下,系統同樣先設定了重啟模式,然後切換到了真實模式,透過一條ljmp $0xffff,$0x0完成了重啟。

3.透過系統呼叫sys_reboot進行關機

在系統呼叫的處理分支上,我們可以看到,首先同樣是檢查MAGIC_NUMBER,然後在

case LINUX_REBOOT_CMD_POWER_OFF:

的執行流程裡面,又是使用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介面關機兩種方法。

4.apm驅動本身的關機過程

apm使用其註冊的裝置的ioctl介面完成apm的操作,在apm.c的do_ioctl函式中可以看見處理的分支。這裡只有suspend和standby的程式碼,所以我們不能透過ioctl這種方法使用apm關機。

當使用者按下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最後呼叫的是關機的流程。

5.解決問題例項

1)按POWER鍵時某些主機板當機

經查只有某些特定的驅動裝載之後才會出現這樣的情況,並且當使用關機系統呼叫sys_reboot的時候沒有這樣的問題。分析apm的處理流程,懷疑是在關機前驅動程式沒有正確處理apm發出的詢問訊息造成的。由於部分驅動程式沒有原始碼,決定hack掉apm.c的關機部分,讓兩種方式的關機走同樣的流程。於是把apm.c的check_events函式中對APM_SYS_SUSPEND部分改寫為如下程式碼:

ret = exec_usermodehelper(poweroff_helper_path, argv, envp);

if (ret) {

printk(KERN_ERR

"apm.c: failed to exec %s , errno = %d ",

poweroff_helper_path, errno);

}

break;

For fast reboot support

static unsigned char fast_reboot_switch [] =

{

0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */

0x66, 0x25, 0x10, 0x11, 0x11, 0x11, /* andl $0x11111110,%eax */

0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */

0xea, 0x00, 0x00, 0x00, 0x70 /* ljmp $0x7000,$0x0000 */

};

系統就可以切換到真實模式中,然後跳轉到7000H:0位置開始執行。

6.ACPI概述

在2.4.20核心中ACPI模組被註明為試驗和未完成,裡面有一部分功能也許沒有實現。如果APM和APCI兩個模組同時編譯進核心,APM在 ACPI前被載入,APM起作用使ACPI退出。對於系統電量、電源實踐一類的支援(主要是在筆記本上有用),靠的是acpid這個daemon程式。

沒有一個功能類似apm的應用程式切換狀態,acpi的程式僅僅完成了對acpi狀態的查詢。使用者實現S0-S4的功能可以直接向/proc/acpi/sleep檔案中寫入數字來實現。透過讀出(cat)其中的內容可以知道系統到底支援那些模式。

acpi模組的原始碼主程式在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中。

7.總結

本文對acpi的介紹非常簡略,實際上ACPI必定會成為將來linux核心中首選的電源管理方式。由於目前官方程式碼中ACPI版本較低,所以沒有太詳細的論述,希望將來的核心能有所改變。

參考資料

linux-2.4.20原始碼

關於作者

範曉炬,聯想(北京)有限公司軟體設計中心嵌入式研發處開發工程師,研究興趣為Linux核心,網路安全,XWindow系統,Linux桌面應用,人工智慧系統。你可以透過xiaoju_f@263.net聯絡他

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-939204/,如需轉載,請註明出處,否則將追究法律責任。

相關文章