編輯:關於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框架的啟動階段。
下面將從Android5.0源碼中,和網絡達人對此的總結中,對此過程加以學習了解和總結,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事件分發機制
Android事件分發,參考了網上的很多資料。基本基於android2.2的源碼來分析,因為即使是新的版本,裡面的原理思想也沒有改變。有了大神的肩膀,我在理解了其原理的基
Android中自定義一個View的方法詳解
本文實例講述了Android中自定義一個View的方法。分享給大家供大家參考,具體如下:Android中自定義View的實現比較簡單,無非就是繼承父類,然後重載方法,即便
Android中操作SQLite數據庫快速入門教程
SQLite是Android平台軟件開發中會經常用到的數據庫產品,作為一款輕型數據庫,SQLite的設計目標就是是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它占用
Android編程之簡單計時器實現方法
本文實例講述了Android編程之簡單計時器實現方法。分享給大家供大家參考,具體如下:這裡利用ContextMenu(上下文菜單),Chronometer實現簡單計數器。