編輯:關於Android編程
從大的方面來說,Android系統的啟動可以分為兩個部分:第一部分是Linux核心的啟動,第二部分是Android系統的啟動。第一部分主要包括系統引導,核心和驅動程序等,由於它們不屬於本篇要講的內容,這裡就不再討論。在本篇博客中,我們重點講解Android系統的啟動,這一過程主要經過兩個階段,分別是應用的初始化流程與system_service進程及核心服務的創建流程。
初始化流程,顧名思義,它完成Android的一些初始化工作,包括設置必要的環境變量,啟動必要的服務進程,掛載必要的設備等,而這些工作將會為整個Android打下堅實的基礎。
在核心啟動完成以後,將進入Android文件系統及應用的初始化流程,此時將會轉向執行init.c中的main()函數(路徑:/system/core/init/init.c),該函數的執行流程如下圖所示:
下面我們了解一下上圖中的注解
注解1:/dev表示設備文件系統或者udev掛載點,/proc用來掛載存放系統過程性信息的虛擬文件系統,/sys用於掛載“sysfs文件系統”。由於前面調用了umask(0),因此mkdir(“/dev”,0755)得到的權限應該是0755.
注解2:init.rc的解析結果是形成action_list(on關鍵字相關的部分),service_list(service_關鍵字相關的部分)以及action_queue(需要執行的命令或服務),以便後續流程使用。
注解3:解析/proc/cmdline文件,將其中的屬性導入Android系統全局變量。
注解4:
Ⅰget_hardware_name()方法用於解析/proc/cpuinfo文件獲取硬件信息,並用於拼接成一個init.
Ⅱ在解析init.rc文件的過程中,系統會根據該文件的內容形成一些需要命令,動作或者觸發器的列表並將這些存入在內在中,以便在必要的時候使用。不同的廠商可能根據不同的硬件需求定制不同的.rc文件,這些.rc文件的名稱一般為“init.
注解5:添加順序為:early-init下的所有動作,wait_for_coldboot_done_action,property_init_action,keychord_init_action,console_init_action,set_init_properties_action,init下的動作,property_service_init_action,signal_init_action,check_startup_action,early-boot下的所有動作,boot下的所有動作,queue_property_triggers_action。這些動作組成了開機過程中看到的設備的狀態,比如開機動畫等。
注解6:這裡會啟動執行設置屬性,創建或掛載動作以及啟動服務等操作。需要注意是的這裡啟動的服務包括最重要的servicemanager和zygote服務進程。
至此,init進程進入死循環中處理一些消息以等待命令的到來。在這個過程中,我們將要了解以下知識。
Ⅰ在init運行的過程中產生了許多服務,它們是整個Android的基礎,分別是ueventd,console,adbd,servicemanager,vold,netd,debuggerd,ril-daemon,surfaceflinger,zygote,drm,media,bootanim,dbus,bluetoothd,installd,flash_recovery,racoon,mtpd,keystore和dumstate。
Ⅱ整個init的行為甚至整個Android核心的屬性都受到啟動腳本init.rc的影響。
下面我們就重點介紹zygote的啟動行為,詳細了解init.rc的語法。
Android初始化語言由聲明的4個類型組成,它們分別是動作(action),命令(command),服務(service),和選項(option),以#開頭的行表示注釋。動作和服務聲明新的一節並且有唯一的名字,所有的命令或者選項屬於最近聲明的節。如果下一個動作或者服務的名字已存在(也就是重名),則它將作為錯誤被忽略。
Ⅰ動作
動作是命令序列,它有一個觸發器,用於確定行動應在何時發生。當發生某一個事件時,它可以匹配到一個動作觸發器,並且該動作會被添加到要執行隊列的尾部(除非它已經在隊列中了)。
隊列中的每個動作是按順序出列的,具體如下所示:
on early-init
write /proc/1/oom-adj -16
setcon u:r:init:s0
start ueventd
動作表現為以下的形式:
on
.........
觸發器是一些字符串,這些字符串可用於匹配一定類型的事件,並且用於觸發動作。下表羅列了一些觸發器的定義。
觸發器 說明 boot 當初始化流程觸發的時候,boot是首先被觸發的動作(在完成/init.conf文件加載之後)
Ⅱ命令
命令是組成動作的成員,也就是說,動作由一個個命令組成。下表羅列了動作支持的命令。
命令 說明 exec
在init.rc中,Android 定義了若干動作,並且這些動作用於完成Android的初始化工作。下面以其中一個動作的配置來說明一下:
on fs
mount yaffs2 mtd@system /system
mount yaffs2 mtd@system /system ro remount
mount yaffs2 mtd@userdata /data nosuid nodev
mount yaffs2 mtd@cache /cache nosuid nodev
這個例子配置了一個觸發器為fs的動作,它由4條命令組成,這4條命令都使用mount命令掛載設備。
Ⅲ服務
服務是一些程序,當它們退出的時候,init啟動並且(選擇性地)重新啟動。服務表現為以下形式:
service
...........
其中各個參數的含義如下所示:
?
?
?[]*:啟動服務所需要的參數,參數個數可以是0個或者多個。
Ⅳ選項
選項是服務的修改器,可以影響如何以及何時初始化運行服務。下表羅列了選項列表。
選項 說明 critical 這是一個對於設備來說比較關鍵的服務,如果它在4分鐘內退出超過4次,那麼設備將重新啟動並進入recovery模式。 disabled 這個服務不能通過類別自動啟動,它必須通過服務名字來顯示啟動 setenv
下面以init.rc文件中的配置為例簡要說明一個服務的配置:
service zygote /system/bin/app_process -Xzygote /system/bin -zygote
--start-system-server
class main
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
在上面代碼中,第一行配置了一個名為zygote的服務,這個服務將會運行/system/bin/app_process,剩余部分為參數(以空格分割)。
剩下的幾行代碼聲明了此服務的選項。這說明zygote是一個類型為main的服務(classmain)並且它會創建一個socket,這個socket的類型為stream,權限為666(socket zygote stream 666)。當重啟此服務的時候,需要完成以下事情。
?寫/sys.android_power/request_state為wake
?寫/sys/power/state為on
?重新啟動media服務
?重新啟動netd服務
init.rc文件需要在init啟動期被解析成系統可以識別的數據結構。前面我們讀懂了init.rc的含義,下面我們就來看看init是如何保存和組織這些信息的,首先,我們來看看在init中如何表示動作,服務和命令,如下表所示:
組件 數據結構 說明 列表節點(listnode) <
最後,我們通過解析init.rc中的一個片段來說明解析過程。
開始解析之前,需要了解整個解析過程至關重要的一個數據結構,那就是parse_state,它保存了整個解析過程中所處的狀態,下圖顯示了它的“成分”
<
③用init解析整個init.rc文件
現在我們 回到init啟動的初期,這裡它調用了init_parse_config_file()方法,而這個方法就是解析init.rc文件的入口。用init解析整個init.rc文件的流程如下圖所示。
下面我們了解一下上圖中的注解、
注解1:state是一個被命名為parser_satte的結構體,用於保存當前文件的解析狀態信息,包括解析的文件(filename),當前解析的行號(line),當前解析的文字指針(ptr),指示下一個動作的變量(nexttoken)以及解析這一行需要的函數指針(parse_line)等。
注解2:next_token()函數位於/system/core/init/parse.c中,用於分析init.rc文件的內容。它只返回3個狀態,分別是:T_EOF(文件結束),T_NEWLINE(一行結束)和T_TEXT(表示遇到第一個空格)。
注解3:init.rc中每一行的信息通過空格被分割為若干段,而這些信息共同組成args[INIT_PARSER_MAXARGS]的內容,並由nargs計數。例如on fs經過解析後,這一行分為兩段(分別是on和fs),分別存放在args中,計數器的值為2.。
注解4:init.rc的每一行經過分割後,需要分析其類型(由lookup_keyword返回)。/system/core/init/keywords.h中定義了所有關鍵字的類型。在片段KEYWORD(on,SECTION,0,0)中,on關鍵字是一個SECTION,有0個(也就是不需要)參數,沒有對應的觸發函數(也就是最後一個0)。
注解5:state.parse_line是一個函數的指針,可以根據關鍵字指向兩種不同的解析方法——parse_line_service(處理服務的選項)和parse_line_action(處理行為的命令)。按照這個流程,init完成整個init.rc文件的解析,並生成service_list和action_list,後續流程所需要的信息將從這兩個列表中獲取,將需要執行的命令或啟動的服務加入action_queue中,這樣就完成了Android系統基礎部分的啟動。
在啟動的過程中,需要特別注意的是,我們通過action_for_each_trigger()方法聲明需要執行的命令隊列,該方法的代碼如下所示:
void action_for_each_trigger(const char * trigger,void (*func)(struct action *act)){
struct listnode * node;
struct action *act;
list_for_each(node,&action_list){
act=node_to_item(node,struct action,alist);
if(!strcmp(act->name,trigger)){
func(act);
}
}
}
在上述代碼中,list_for_each()用於遍歷action_list中的每一個節點,返回節點在列表中的位置信息,然後通過node_to_item()方法生成一個action的信息,最後執行func()函數。
action_for_each_trigger()方法在init.rc中是這樣調用的:
action_for_each_trigger(early-init,action_add_queue_tail);
它的含義是,在action_list中查找名字為early-init的節點,並將其信息通過action_add_queue_tail()方法加入action_queue隊列的尾部。
然後在init的無限循環中遍歷action_queue中的每一個節點,執行它們所包含的命令。
講到這裡,我們了解了init如何對待init.rc文件的內容。下面擴展一下知識,概要介紹一下Android系統中*.rc文件的關鍵字及其使用需求,如下表。如果你想修改或編寫自己的.rc文件,那麼請關注下表。
關鍵字 類型 參數個數 capability OPTION 0 chdir COMMAND 1 chroot COMMAND 1 class OPTION 0 class_start COMMAND 1 class_stop COMMAND 1 class_reset COMMAND 1 console OPTION 0 critical OPTION 0 disabled OPTION 0 domainname COMMAND 1 exec COMMAND 1 export COMMAND 2 group OPTION 0 hostname COMMAND 1 ifup COMMAND 1 insmod COMMAND 1 import SECTION 1 keycodes OPTION 0 mkdir COMMAND 1 mount COMMAND 3 on SECTION 0 oneshot OPTION 0 onrestart OPTION 0 restart COMMAND 1 rm COMMAND 1 rmdir COMMAND 1 service SECTION 0 setenv OPTION 2 setkey COMMAND 0 setprop COMMAND 2 setrlimit COMMAND 3 socket OPTION 0 start COMMAND 1 stop COMMAND 1 trigger COMMAND 1 Symlink COMMAND 1 sysclktz COMMAND 1 user OPTION 0 wait COMMAND 1 write COMMAND 2 copy COMMAND 2 chown COMMAND 2 chmod COMMAND 2 loglevel COMMAND 1 load_persist_props COMMAND 0 ioprio OPTION 0
在init進程的啟動過程中,比較重要的部分由孵化進程啟動system_service進程,下面詳細介紹一下這個部分。system_service進程將會為我們創建一些重要的Android核心服務,包括ActivityManagerService,PackageManagerService和PowerManagerService等,這些將成為應用程序的基礎,並為應用程序提供必要的接口。
完成應用程序的初始化之後,init進程將創建一個名叫system_service的重要進程,而我們將在此進程中創建Android核心服務。下圖顯示了system_process進程以及核心服務的創建過程。
注解1:init進程會按順序啟動各種類型的服務(包括core和main)。首先啟動core類型的服務。然後啟動main類型的服務。由於孵化服務為main類型,所以它會在core類型的服務之後啟動。因此,這裡啟動用於管理服務的服務——servicemanager。啟動和入口如下所示。
?啟動:service zygote /system/bin/app_process -Xzygote /system/bin --zygote--start-system-server
?入口:/frameworks/base/cmds/app_process/app_main.cpp的main()函數。
注解2:此時轉向/frameworks/base/core/jni/AndroidRuntime.cpp的start()函數。
注解3:啟動代碼如下:
jmethodId startMeth=env->GetStaticMethodID(startClass,main,....);
env->CallStaticVoidMethod(startClass,startMeth,strArray);
此時轉向com.android.internal.os.ZygoteInit的main()方法執行。
注解4:
?加載frameworks下的preloaded-classes類。
?加載framework-res.apk下的資源。
注解5:孵化進程的主要目的就是孵化出system_process進程,這個時候流程將轉向/frameworks/base/services/java/com/android/server/SystemServer.java的main()方法執行,而自身進入死循環成為守護進程。
注解6:init1()調用本地android_server_SystemServer_init1(/frameworks/base/services/jni/com_android_server_SystemServer.cpp)後,通過libAndroid_servers.So的system_init()函數啟動兩個服務並啟動init2()、
注解7:這裡啟動並注冊剩余的必需服務(比如包服務和Activity服務等)。最終會啟動Launcher來到桌面,至此整個啟動過程完成。
system_service進程非常重要,它創建了許多重要的服務,那麼如何加入system_service中並接受管理呢?具體如下面的代碼所示:
try{
Slog.i(TAG,Backup Service);
ServiceManager.addService(Context.BACKUP_SERVICE,new BackupManagerService(context));
}catch(Throwable e){
Slog.e(TAG,Failure starting Backup Service,e);
}
以上代碼展示了system_process如何將備份服務加入服務管理器中的,其中粗體部分的代碼完成了兩件事情:第一,創建備份服務;第二,使用ServiceManager的addService()方法將創建出來的備份服務實例加入服務管理器中加以管理。
下表列出了system_service的服務關鍵字等知識。
服務關鍵字 類 備注 entropy EntropyService 熵服務 power PowerManagerService 電源管理服務(Context.POWER_SERVICE) activity ActivityManagerService Activity管理服務 telephony.registry TelephonyRegistry 電話服務 package PackageManagerService 包管理服務 account AccountManagerService 賬戶管理服務(Context.ACCOUT_SERVICE) battery BatteryService 電池服務 vibrator VibratorService 振動服務 alarm AlarmManagerService 報警服務(Context.ALARM_SERVICE) window WindowManagerService 窗口服務(Context.WINDOW_SERVICE) bluetooth BluetoothService 藍牙服務(BluetoothAdapter.BLUETOOTH_SERVICE) statusbar StatusBarManagerService 狀態欄服務(Context.STATUS_BAR_SERVICE) input_method InputMethodManagerService 輸入法管理服務(Context.INPUT_METHOD_SERVICE) location LocationManagerService 位置管理服務(Context.LOCATION_SERVICE) wallpaper WallpaperManagerService 壁紙管理服務(Context.WALLPAPER_SERVICE) audio AudioService 聲音服務(Context.AUDIO_SERVICE) user UserManagerService 用戶管理服務(Context.USER_SERVICE)
AudioService as=(AudioService)context.getSystemService(Context.AUDIO_SERVICE);
此時整個系統也就完成了啟動工作,這也意味著我們可以開始使用Android設備了。
本文實例講述了Android動畫之補間動畫。分享給大家供大家參考,具體如下:前面講了《Android動畫之逐幀動畫(Frame Animation)》,今天就來詳細講解一
前言很久沒寫BLOG了,之前在寫Android聊天室的時候答應過要寫一個客戶(好友)之間的聊天demo,Android 基於Socket的聊天室已經實現了通過Socket
概述 移動互聯網安全無疑已成為當今主流安全威脅之一。統計 2011 年至今的移動惡意代碼病毒庫,可以看到 Android 平台下的惡意軟件數量增長可謂
每次應用程序運行時,應用程序的application類保持實例化的狀態。通過擴展applicaiton類,可以完成以下3項工作: 1.對android運行時廣播的應用程序