編輯:Android開發實例
一、Android Init.c執行流程
Android中的內核啟動後,kernel會啟動第一個用戶級別的進程:init,它是一個由內核啟動的用戶級進程。內核自行啟動(已經被載入內存,開始運行,並已初始化所有的設備驅動程序和數據結構等)之後,就通過啟動一個用戶級程序init的方式,完成引導進程。init始終是第一個進程。
PS:可以通過:ps aux | grep init命令來查看其Pid為1。
init進程對應的代碼在android源碼目錄中的:system/core/init/init.c中。
789 int main(int argc, char **argv)
790 {
# 創建一些linux根文件系統中的目錄
817 mkdir("/dev", 0755);
818 mkdir("/proc", 0755);
819 mkdir("/sys", 0755);
820
821 mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
822 mkdir("/dev/pts", 0755);
823 mkdir("/dev/socket", 0755);
824 mount("devpts", "/dev/pts", "devpts", 0, NULL);
825 mount("proc", "/proc", "proc", 0, NULL);
826 mount("sysfs", "/sys", "sysfs", 0, NULL);
# 打開標准輸入,標准輸出,標准錯誤文件描述符
834 open_devnull_stdio();
# 讀取並且解析init.rc文件
838 parse_config_file("/init.rc");
# 取得硬件名
844 get_hardware_name();
845 snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
# 讀取並且解析硬件相關的init腳本文件
846 parse_config_file(tmp);
# 觸發在init腳本文件中名字為early-init的action,並且執行其commands,其實是: on early-init
848 action_for_each_trigger("early-init", action_add_queue_tail);
849 drain_action_queue();
# 初始化動態設備管理,設備文件有變化時反應給內核,後面具體解釋
852 device_fd = device_init();
# 加載啟動動畫,如果動畫打開失敗,則在屏幕上打印: A N D R O I D字樣。
872 if( load_565rle_image(INIT_IMAGE_FILE) ) {
873 fd = open("/dev/tty0", O_WRONLY);
874 if (fd >= 0) {
875 const char *msg;
876 msg = "\n"
877 "\n"
878 "\n"
879 "\n"
880 "\n"
881 "\n"
882 "\n" // console is 40 cols x 30 lines
883 "\n"
884 "\n"
885 "\n"
886 "\n"
887 "\n"
888 "\n"
889 "\n"
890 //" A N D R O I D ";
891 write(fd, msg, strlen(msg));
892 close(fd);
893 }
894 }
895
# 觸發在init腳本文件中名字為init的action,並且執行其commands,其實是:on init
919 action_for_each_trigger("init", action_add_queue_tail);
920 drain_action_queue();
# 啟動系統屬性服務: system property service
927 property_set_fd = start_property_service();
# 創建socket用來處理孤兒進程信號
930 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
931 signal_fd = s[0];
932 signal_recv_fd = s[1];
933 fcntl(s[0], F_SETFD, FD_CLOEXEC);
934 fcntl(s[0], F_SETFL, O_NONBLOCK);
935 fcntl(s[1], F_SETFD, FD_CLOEXEC);
936 fcntl(s[1], F_SETFL, O_NONBLOCK);
937 }
# 觸發在init腳本文件中名字為early-boot和boot的action,並且執行其commands,其實是:on early-boot和on boot
948 action_for_each_trigger("early-boot", action_add_queue_tail);
949 action_for_each_trigger("boot", action_add_queue_tail);
950 drain_action_queue();
# 啟動所有屬性變化觸發命令,其實是: on property:ro.xx.xx=xx
953 queue_all_property_triggers();
954 drain_action_queue();
# 進入死循環
987 for(;;) {
# 啟動所有init腳本中聲明的service,
# 如:266 service servicemanager /system/bin/servicemanager
# user system
# critical
# onrestart restart zygote
# onrestart restart media
994 restart_processes();
# 多路監聽設備管理,子進程運行狀態,屬性服務
1012 nr = poll(ufds, fd_count, timeout);
1013 if (nr <= 0)
1014 continue;
1016 if (ufds[2].revents == POLLIN) {
1017 /* we got a SIGCHLD - reap and restart as needed */
1018 read(signal_recv_fd, tmp, sizeof(tmp));
1019 while (!wait_for_one_process(0))
1020 ;
1021 continue;
1022 }
1023
1024 if (ufds[0].revents == POLLIN)
1025 handle_device_fd(device_fd);
1026
1027 if (ufds[1].revents == POLLIN)
1028 handle_property_set_fd(property_set_fd);
1029 if (ufds[3].revents == POLLIN)
1030 handle_keychord(keychord_fd);
1031 }
1032
1033 return 0;
1034 }
二、Android init腳本語言
前面簡單分析了下init.c裡的操作,裡面提到了解析Init.rc和硬件腳本下面詳細解析下Android init腳本語言的規范。
Android初始化語言包含了四種類型的聲明:
Actions(行為)、Commands(命令)、Services(服務)和Options(選項)
Actions和Services聲明一個新的分組Section。所有的命令或選項都屬於最近聲明的分組。位於第一個分組之前的命令或選項將會被忽略。
Actions和Services有唯一的名字。如果有重名的情況,第二個申明的將會被作為錯誤忽略。
Actions(行為)
Actions其實就是一序列的Commands(命令)。Actions都有一個trigger(觸發器),它被用於決定action的執行時間。當一個符合action觸發條件的事件發生時,action會被加入到執行隊列的末尾,除非它已經在隊列裡了。
隊列中的每一個action都被依次提取出,而這個action中的每個command(命令)都將被依次執行。
Actions的形式如下:
on <trigger>
<command1>
<command2>
<command3>
on後面跟著一個觸發器,當trigger被觸發時,command1,command2,command3,會依次執行,直到下一個Action或下一個Service。
簡單來說,Actions就是Android在啟動時定義的一個啟動腳本,當條件滿足時,會執行該腳本,腳本裡都是一些命令commands,不同的腳本用on來區分。
Triggers(觸發器)
Triggers(觸發器)是一個用於匹配特定事件類型的字符串,用於使Actions發生。
boot:
這是init執行後的第一個被觸發的Triggers(觸發器)。(在 /init.conf (啟動配置文件)被裝載之後)
<name>=<value>:
這種形式的Triggers(觸發器)會在屬性<name>被設置為指定的<value>時被觸發。
device-added-<path>:
device-removed-<path>:
這種形式的Triggers(觸發器)會在一個設備節點文件被增刪時觸發。
service-exited-<name>:
這種形式的Triggers(觸發器)會在一個特定的服務退出時觸發。
觸發器通常和on一起來聯合使用。
示例:
on init
export LD_LIBRARY_PATH /system/lib
insmod modules/fsr.ko
symlink /system/etc /etc
mkdir /sdcard 0000 system system
write /proc/cpu/alignment 4
on boot
…
on property:ro.kernel.qemu=1
start adbd
上面聲明了三個action:init,boot和一個屬性觸發器,當init被觸發時,會順序執行後面的命令,直到on boot新的action。Init的觸發是由init.c裡的函數action_for_each_trigger來決定的。當屬性ro.kernel.qemu為1 時,會觸發start adbd命令。
Services(服務)
Services(服務)是一個程序,它在初始化時啟動,並在退出時可選擇讓其重啟。Services(服務)的形式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
name:服務名
pathname:當前服務對應的程序位置
option:當前服務設置的選項
Options(選項)
Options(選項)是一個Services(服務)的修正者。他們影響Services(服務)在何時,並以何種方式運行。
critical:
說明這是一個對於設備關鍵的服務。如果他四分鐘內退出大於四次,系統將會重啟並進入recovery(恢復)模式。
disabled:
說明這個服務不會同與他同trigger(觸發器)下的服務自動啟動。他必須被明確的按名啟動。
setenv <name> <value> (設置環境變量)
在進程啟動時將環境變量<name>設置為<value>。
socket <name> <type> <perm> [ <user> [ <group> ] ]
創建一個Uinx域的名為/dev/socket/<name> 的套接字,並傳遞它的文件描述符給已啟動的進程。<type> 必須是 "dgram"或"stream"。User 和 group默認為0。
user <username>
在啟動這個服務前改變該服務的用戶名。此時默認為root。(???有可能的話應該默認為nobody)。當前,如果你的進程要求Linux capabilities(能力),你無法使用這個命令。即使你是root,你也必須在程序中請求capabilities(能力)。然後降到你想要的 uid。
group <groupname> [ <groupname> ]*
在啟動這個服務前改變該服務的組名。除了(必需的)第一個組名,附加的組名通常被用於設置進程的補充組(通過setgroups())。此時默認為root。(???有可能的話應該默認為nobody)。
oneshot
服務退出時不重啟。
class <name>
指定一個服務類。所有同一類的服務可以同時啟動和停止。如果不通過class選項指定一個類,則默認為"default"類服務。
onrestart
當服務重啟,執行一個命令(下詳)。
Commands(命令)
exec <path> [ <argument> ]*
創建和執行一個程序(<path>)。在程序完全執行前,init將會阻塞。由於它不是內置命令,應盡量避免使用exec,它可能會引起init卡死。(??? 是否需要一個超時設置?)
export <name> <value>
在全局環境變量中設在環境變量 <name>為<value>。(這將會被所有在這命令之後運行的進程所繼承)
ifup <interface>
啟動網絡接口<interface>
import <filename>
解析一個init配置文件,擴展當前配置。
hostname <name>
設置主機名。
chmod <octal-mode> <path>
更改文件訪問權限。
chown <owner> <group> <path>
更改文件的所有者和組。
class_start <serviceclass>
啟動所有指定服務類下的未運行服務。
class_stop <serviceclass>
停止指定服務類下的所有已運行的服務。
domainname <name>
設置域名。
insmod <path>
加載<path>中的模塊。
mkdir <path> [mode] [owner] [group]
創建一個目錄<path>,可以選擇性地指定mode、owner以及group。如果沒有指定,默認的權限為755,並屬於root用戶和root組。
mount <type> <device> <dir> [ <mountoption> ]*
試圖在目錄<dir>掛載指定的設備。<device> 可以是以 [email protected] 的形式指定一個mtd塊設備。<mountoption>包括 "ro"、"rw"、"remount"、"noatime"、 ...
setprop <name> <value>
設置系統屬性 <name> 為 <value>值.
setrlimit <resource> <cur> <max>
設置<resource>的rlimit(資源限制)。
start <service>
啟動指定服務(如果此服務還未運行)。
stop <service>
停止指定服務(如果此服務在運行中)。
symlink <target> <path>
創建一個指向<path>的軟連接<target>。
sysclktz <mins_west_of_gmt>
設置系統時鐘基准(0代表時鐘滴答以格林威治平均時(GMT)為准)
trigger <event>
觸發一個事件。用於將一個action與另一個 action排列。
write <path> <string> [ <string> ]*
打開路徑為<path>的一個文件,並寫入一個或多個字符串。
Properties(屬性)
Init更新一些系統屬性以提供對正在發生的事件的監控能力:
init.action
此屬性值為正在被執行的action的名字,如果沒有則為""。
init.command
此屬性值為正在被執行的command的名字,如果沒有則為""。
init.svc.<name>
名為<name>的service的狀態("stopped"(停止), "running"(運行), "restarting"(重啟))
三、Android init 示例
1 on init
2
3 sysclktz 0
4
5 loglevel 3
6
7 # setup the global environment
8 export PATH /system/busybox/bin:/system/busybox/sbin:/system/busybox/usr/bin:/system/busybox/usr/sbin:/sbin:/system/sbin:/system/bin:/system/xbin
9 export LD_LIBRARY_PATH /system/lib
10 export ANDROID_BOOTLOGO 1
11 export ANDROID_ROOT /system
12 export ANDROID_ASSETS /system/app
13 export ANDROID_DATA /data
14 export EXTERNAL_STORAGE /sdcard
... ...
23 symlink /system/etc /etc
24 symlink /sys/kernel/debug /d
25
26 # create mountpoints and mount tmpfs on sqlite_stmt_journals
27 mkdir /sdcard 0000 system system
28 mkdir /system
29 mkdir /data 0771 system system
30 mkdir /cache 0770 system cache
31 mkdir /config 0500 root root
32 mkdir /sqlite_stmt_journals 01777 root root
33 mount tmpfs tmpfs /sqlite_stmt_journals size=4m
34
35 # mount rootfs rootfs / ro remount
36
37 write /proc/sys/kernel/panic_on_oops 1
38 write /proc/sys/kernel/hung_task_timeout_secs 0
39 write /proc/cpu/alignment 4
40 write /proc/sys/kernel/sched_latency_ns 10000000
41 write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
42 write /proc/sys/kernel/sched_compat_yield 1
43 write /proc/sys/kernel/sched_child_runs_first 0
44
45 # Create cgroup mount points for process groups
46 mkdir /dev/cpuctl
47 mount cgroup none /dev/cpuctl cpu
48 chown system system /dev/cpuctl
49 chown system system /dev/cpuctl/tasks
50 chmod 0777 /dev/cpuctl/tasks
51 write /dev/cpuctl/cpu.shares 1024
52
53 mkdir /dev/cpuctl/fg_boost
54 chown system system /dev/cpuctl/fg_boost/tasks
55 chmod 0777 /dev/cpuctl/fg_boost/tasks
56 write /dev/cpuctl/fg_boost/cpu.shares 1024
57
58 mkdir /dev/cpuctl/bg_non_interactive
59 chown system system /dev/cpuctl/bg_non_interactive/tasks
60 chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
61 # 5.0 %
62 write /dev/cpuctl/bg_non_interactive/cpu.shares 52
... ...
146 on boot
147 # basic network init
148 ifup lo
149 hostname localhost
150 domainname localdomain
151
152 # set RLIMIT_NICE to allow priorities from 19 to -20
153 setrlimit 13 40 40
154
155 # Define the oom_adj values for the classes of processes that can be
156 # killed by the kernel. These are used in ActivityManagerService.
157 setprop ro.FOREGROUND_APP_ADJ 0
158 setprop ro.VISIBLE_APP_ADJ 1
159 setprop ro.SECONDARY_SERVER_ADJ 2
160 setprop ro.BACKUP_APP_ADJ 2
161 setprop ro.HOME_APP_ADJ 4
162 setprop ro.HIDDEN_APP_MIN_ADJ 7
... ...
233 # Define TCP buffer sizes for various networks
234 # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
235 setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
236 setprop net.tcp.buffersize.wifi 4095,87380,110208,4096,16384,110208
237 setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208
238 setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040
239 setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680
240 setprop net.dns1 8.8.8.8
241 setprop net.eth0.dns1 8.8.8.8
242
243 class_start default
... ...
247 service console /system/bin/sh
248 console
249
252 service adbd /sbin/adbd
253 disabled'
259 on property:persist.service.adb.enable=1
260 start adbd
261
262 on property:persist.service.adb.enable=0
263 stop adbd
265 service servicemanager /system/bin/servicemanager
266 user system
267 critical
268 onrestart restart zygote
269 onrestart restart media
271 service vold /system/bin/vold
272 socket vold stream 0660 root mount
282 service ril-daemon /system/bin/rild
283 socket rild stream 660 root radio
284 socket rild-debug stream 660 radio system
285 user root
286 group radio cache inet misc
287
288 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
289 socket zygote stream 666
290 onrestart write /sys/android_power/request_state wake
291 onrestart write /sys/power/state on
292 onrestart restart media
293
294 service media /system/bin/mediaserver
295 user media
296 group system audio camera graphics inet net_bt net_bt_admin
297
298 service bootsound /system/bin/playmp3
299 user media
300 group audio
301 oneshot
302
303 service bootanim /system/bin/bootanimation
304 user graphics
305 group graphics
306 disabled
307 oneshot
... ...
由上面的例子可知:該init.rc裡定義了4個actions:
on init
on boot
on property:persist.service.adb.enable=1
on property:persist.service.adb.enable=0
其中,前兩個的觸發器是由Init.c裡的代碼action_for_each_trigger實現觸發的,後兩個是由屬性的值發生改變時觸發的。當他們被觸發時,會依次執行其後面的值,直到遇到一個新的Action或service。
該init.rc中定義了9個services,這些service屬於本地服務,它們都是由c或c++代碼寫的,以可執行文件的方式存在文件系統裡的/system/bin或/sbin目錄下。由前面知識可知,如果一個服務沒有使用class選項指定其分類,則其默認分類為:default,那麼這9個services的分類為default。在on boot action觸發時,其最後一個命令是class_start default,也就是將這9個services都依次啟動。
init.rc中的9個service裡,以zygote和servicemanager最為重要。
1> zygote service
該service主要用於創建Dalvik Java虛擬機,然後啟動SystemServer,啟動所有的Android服務,最終啟動Android系統。
2> servicemanager
該service主要用來打開binder驅動,binder是Android Service通信機制的底層實現,在該service裡封裝了binder的操作接口。
技巧:
在調試或理解系統的工作原理的時候,我們經常要去找服務程序對應的源碼。
尋找c或c++程序的源碼文件:
例如:以尋找init程序對應的源碼為例。
find ./ -name Android.mk -exec grep -l init {} \;
注:通過find命令查找所有的Android.mk, 通過grep從中查找程序字符串,得到其路徑,然後去路徑下找源碼即可,這麼做的原因是,c或c++代碼都是通過Android.mk來指導編譯的。
尋找java源碼文件:
java源碼的特點是和類名一致,所以如果我們知道一個類名,找其java源碼就直接加上java後綴即可。
例如:尋找com.android.internal.os.ZygoteInit類的代碼。
find ./ -name ZygoteInit.java
分類 notification有以下幾種: 1>普通notification 1.內容標題 2.大圖標 3.內
TableLayout經常用到的屬性有: android:collapseColumns:以第0行為序,隱藏指定的列: android:collapseColum
本文主要講解利用android中Matrix控制圖形的旋轉縮放移動,具體參見一下代碼:代碼如下:/** * 使用矩陣控制圖片移動、縮放、旋轉 &nbs
本文實例講述了Android編程之菜單實現方法。分享給大家供大家參考,具體如下: 菜單是許多應用程序不可或缺的一部分,Android中更是如此,所有搭載Andro