編輯:關於Android編程
As we all know,Android手機系統本質上是一個基於Linux的應用程序,它以Linux系統為內核。因此系統的啟動過程包括Linux內核啟動和Android框架啟動兩個階段。
1、裝載引導程序bootloader
Linux內核啟動時首先裝載執行bootloader引導程序,裝載完成後進入內核程序。
2、加載Linux內核
Linux內核加載主要包括初始化kernel核心(內存初始化,打開中斷,初始化進程表等)、初始化驅動、啟動內核後台(daemons)線程、安裝根(root)文件系統等。
Linux加載的最後階段啟動執行第一個用戶級進程init(內核引導參數上一般都會設置“init=/init”,由kernel自動執行,PID為1,是所有進程的父進程)。由此進入Android框架的啟動階段。
init是一個進程,確切地說,它是Linux系統中用戶空間的第一個進程。由於Android是基於Linux內核的,所以init也是Android系統中用戶空間的第一個進程,它的進程號是1。
init進程的入口函數是main,system\core\init\init.c
init進程可以在/system/core/init找到。
init.rc文件可以在/system/core/rootdir/init.rc找到。
readme.txt可以在/system/core/init/readme.txt找到。
int main(int argc, char **argv) { intdevice_fd = -1; intproperty_set_fd = -1; intsignal_recv_fd = -1; intkeychord_fd = -1; int fd_count; ints[2]; intfd; structsigaction act; chartmp[PROP_VALUE_MAX]; structpollfd ufds[4]; char*tmpdev; char*debuggable; //設置子進程退出的信號處理函數,該函數為sigchld_handler。 act.sa_handler = sigchld_handler; act.sa_flags= SA_NOCLDSTOP; act.sa_mask = 0; act.sa_restorer = NULL; sigaction(SIGCHLD, &act, 0); ......//創建一些文件夾,並掛載設備,這些是和Linux相關的,不擬做過多討論。 mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0,NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); //重定向標准輸入/輸出/錯誤輸出到/dev/_null_。 open_devnull_stdio(); /* 設置init的日志輸出設備為/dev/__kmsg__,不過該文件打開後,會立即被unlink了, 這樣,其他進程就無法打開這個文件讀取日志信息了。 */ log_init(); //上面涉及很多和Linux系統相關的知識,不熟悉的讀者可自行研究,它們不影響我們的分析 //解析init.rc配置文件 parse_config_file("/init.rc"); ...... //下面這個函數通過讀取/proc/cpuinfo得到機器的Hardware名,我的HTCG7手機為bravo。 get_hardware_name(); snprintf(tmp,sizeof(tmp), "/init.%s.rc", hardware); //解析這個和機器相關的配置文件,我的G7手機對應文件為init.bravo.rc。 parse_config_file(tmp); /* 解析完上述兩個配置文件後,會得到一系列的Action(動作),下面兩句代碼將執行那些處於 early-init階段的Action。init將動作執行的時間劃分為四個階段:early-init、init、 early-boot、boot。由於有些動作必須在其他動作完成後才能執行,所以就有了先後之分。哪些 動作屬於哪個階段由配置文件決定。後面會介紹配置文件的相關知識。 */ action_for_each_trigger("early-init", action_add_queue_tail); drain_action_queue(); /* 創建利用Uevent和Linux內核交互的socket。關於Uevent的知識,第9章中對 Vold進行分析時會做介紹。 */ device_fd = device_init(); //初始化和屬性相關的資源 property_init(); //初始化/dev/keychord設備,這和調試有關,本書不討論它的用法。讀者可以自行研究, //內容比較簡單。 keychord_fd = open_keychord(); ...... /* INIT_IMAGE_FILE定義為”/initlogo.rle”,下面這個函數將加載這個文件作為系統的開機 畫面,注意,它不是開機動畫控制程序bootanimation加載的開機動畫文件。 */ if(load_565rle_image(INIT_IMAGE_FILE) ) { /* 如果加載initlogo.rle文件失敗(可能是沒有這個文件),則會打開/dev/ty0設備,並 輸出”ANDROID”的字樣作為開機畫面。在模擬器上看到的開機畫面就是它。 */ ...... } } if(qemu[0]) import_kernel_cmdline(1); ...... //調用property_set函數設置屬性項,一個屬性項包括屬性名和屬性值。 property_set("ro.bootloader", bootloader[0] ? bootloader :"unknown"); ......//執行位於init階段的動作 action_for_each_trigger("init", action_add_queue_tail); drain_action_queue(); //啟動屬性服務 property_set_fd = start_property_service(); /* 調用socketpair函數創建兩個已經connect好的socket。socketpair是Linux的系統調用, 不熟悉的讀者可以利用man socketpair查詢相關信息。後面就會知道它們的用處了。 */ if(socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { signal_fd = s[0]; signal_recv_fd = s[1]; ...... } ...... //執行配置文件中early-boot和boot階段的動作。 action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); drain_action_queue(); ...... //init關注來自四個方面的事情。 ufds[0].fd= device_fd;//device_fd用於監聽來自內核的Uevent事件 ufds[0].events = POLLIN; ufds[1].fd = property_set_fd;//property_set_fd用於監聽來自屬性服務器的事件 ufds[1].events= POLLIN; //signal_recv_fd由socketpair創建,它的事件來自另外一個socket。 ufds[2].fd = signal_recv_fd; ufds[2].events = POLLIN; fd_count = 3; if(keychord_fd > 0) { //如果keychord設備初始化成功,則init也會關注來自這個設備的事件。 ufds[3].fd = keychord_fd; ufds[3].events = POLLIN; fd_count++; } ...... #if BOOTCHART ......//與Boot char相關,不做討論了。 /* Boot chart是一個小工具,它能對系統的性能進行分析,並生成系統啟動過程的圖表, 以提供一些有價值的信息,而這些信息最大的用處就是幫助提升系統的啟動速度。 */ #endif for(;;) { //從此init將進入一個無限循環。 int nr, i, timeout = -1; for (i = 0; i < fd_count; i++) ufds[i].revents = 0; //在循環中執行動作 drain_action_queue(); restart_processes(); //重啟那些已經死去的進程 ...... #if BOOTCHART ...... // Boot Chart相關 #endif //調用poll等待一些事情的發生 nr= poll(ufds, fd_count, timeout); ...... //ufds[2]保存的是signal_recv_fd,用於接收來自socket的消息。 if(ufds[2].revents == POLLIN) { //有一個子進程去世,init要處理這個事情 read(signal_recv_fd, tmp, sizeof(tmp)); while (!wait_for_one_process(0)) ; continue; } if(ufds[0].revents == POLLIN) handle_device_fd(device_fd);//處理Uevent事件 if(ufds[1].revents == POLLIN) handle_property_set_fd(property_set_fd);//處理屬性服務的事件。 if(ufds[3].revents == POLLIN) handle_keychord(keychord_fd);//處理keychord事件。 } return0; }
這個函數摘抄過來已經精簡了不少。總的來說,在函數中執行了:文件夾建立,掛載,rc文件解析,屬性設置,啟動服務,執行動作,socket監聽……
總的來說init的工作流程精簡為以下幾點:
創建一些文件夾並掛載設備
解析兩個配置文件init.rc和init.hardware.rc,其中,將分析對init.rc文件的解析。init進入一個無限循環,並且等待一些事情的發生。重點關注init如何處理來自socket和來自屬性服務器相關的事情。
對於init.rc文件,Android中有特定的格式以及規則。在Android中,我們叫做Android初始化語言。
Android初始化語言由四大類型的聲明組成,即Actions(動作)、Commands(命令)、Services(服務)、以及Options(選項)。
Action(動作):動作是以命令流程命名的,有一個觸發器決定動作是否發生。
語法
on
語法
service
Action/Service 描述
on early-init 設置init進程以及它創建的子進程的優先級,設置init進程的安全環境
on init 設置全局環境,為cpu accounting創建cgroup(資源控制)掛載點
on fs 掛載mtd分區
on post-fs 改變系統目錄的訪問權限
on post-fs-data 改變/data目錄以及它的子目錄的訪問權限
on boot 基本網絡的初始化,內存管理等等
service servicemanager 啟動系統管理器管理所有的本地服務,比如位置、音頻、Shared preference等等…
service zygote 啟動zygote作為應用進程
在這個階段你可以在設備的屏幕上看到“Android”logo了。
需要重點說明的是init.rc腳本文件配置了一些重要的服務,init進程通過創建子進程啟動這些服務,這裡創建的service都屬於native服務,運行在Linux空間,通過socket向上層提供特定的服務,並以守護進程的方式運行在後台。腳本中服務的定義示例如下:
service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart zygote 08 onrestart restart media service ril-daemon /system/bin/rild class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root group radio cache inet misc audio sdcard_rw log service surfaceflinger /system/bin/surfaceflinger class main user system group graphics onrestart restart zygote 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 service media /system/bin/mediaserver class main user media group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc ioprio rt 4
int do_class_start(int nargs, char **args) { service_for_each_class(args[1], service_start_if_not_disabled); return 0; } 遍歷所有名稱為classname,狀態不為SVC_DISABLED的Service啟動 void service_for_each_class(const char *classname, void (*func)(struct service *svc)) { …… }
static void service_start_if_not_disabled(struct service *svc) { if (!(svc->flags & SVC_DISABLED)) { service_start(svc, NULL); } }
int main(){ //掛載文件 //解析配置文件:init.rc…… //初始化化action queue …… for(;;){ execute_one_command(); restart_processes(); for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } }
public void systemReady(final Runnable goingCallback) { …… //ready callback if (goingCallback != null) goingCallback.run(); synchronized (this) { // Start up initial activity. // ActivityStack mMainStack; mMainStack.resumeTopActivityLocked(null); } …… } final boolean resumeTopActivityLocked(ActivityRecord prev) { // Find the first activity that is not finishing. ActivityRecord next = topRunningActivityLocked(null); if (next == null) { // There are no more activities! Let's just start up the // Launcher... if (mMainStack) { //ActivityManagerService mService; return mService.startHomeActivityLocked(); } } …… }
Android事件分發,參考了網上的很多資料。基本基於android2.2的源碼來分析,因為即使是新的版本,裡面的原理思想也沒有改變。有了大神的肩膀,我在理解了其原理的基
本文實例講述了Android中自定義一個View的方法。分享給大家供大家參考,具體如下:Android中自定義View的實現比較簡單,無非就是繼承父類,然後重載方法,即便
SQLite是Android平台軟件開發中會經常用到的數據庫產品,作為一款輕型數據庫,SQLite的設計目標就是是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它占用
本文實例講述了Android編程之簡單計時器實現方法。分享給大家供大家參考,具體如下:這裡利用ContextMenu(上下文菜單),Chronometer實現簡單計數器。