主要介紹linux 內(nèi)核啟動(dòng)過程以及掛載android 根文件系統(tǒng)的過程,以及介紹android 源代碼中文件系統(tǒng)部分的淺析。
主要源代碼目錄介紹
Makefile (全局的Makefile)
bionic (Bionic 含義為仿生,這里面是一些基礎(chǔ)的庫的源代碼)
bootable (引導(dǎo)加載器)
build (build 目錄中的內(nèi)容不是目標(biāo)所用的代碼,而是編譯和配置所需要的腳本和工具)
dalvik (JAVA 虛擬機(jī))
development (程序開發(fā)所需要的模板和工 具) external (目標(biāo)機(jī)器使用的一些庫)
frameworks (應(yīng)用程序的框架層)
hardware (與硬件相關(guān)的庫)
packages (Android 的各種應(yīng)用程序)
prebuilt (Android 在各種平臺(tái)下編譯的預(yù)置腳本)
recovery (與目標(biāo)的恢復(fù)功能相關(guān))
system (Android 的底層的一些庫)
out (編譯完成后產(chǎn)生的目錄,也就是我們移植文件系統(tǒng)需要的目錄)
host 目錄的結(jié)構(gòu)如下所示:
out/host/
|-- common
| `-- obj (JAVA 庫)
`-- linux-x86
|-- bin (瞥絳潁?br> |-- framework (JAVA 庫,*.jar 文件)
|-- lib (共享庫*.so)
`-- obj (中間生成的目標(biāo)文件)
host 目錄是一些在主機(jī)上用的工具,有一些是二進(jìn)制程序,有一些是JAVA 的程序。
target 目錄的結(jié)構(gòu)如下所示:
out/target/
|-- common
| |-- R (資源文件)
| |-- docs
| `-- obj (目標(biāo)文件)
`-- product
`-- generic
其中common 目錄表示通用的內(nèi)容,product 中則是針對產(chǎn)品的內(nèi)容。
在common 目錄的obj 中,包含兩個(gè)重要的目錄:
APPS 中包含了JAVA 應(yīng)用程序生成的目標(biāo),每個(gè)應(yīng)用程序?qū)?yīng)其中一個(gè)子目錄,將結(jié)合每個(gè)應(yīng)用程序的原始文件生成Android 應(yīng)用程序的APK 包。 JAVA_LIBRARIES 中包含了JAVA 的庫,每個(gè)庫對應(yīng)其中一個(gè)子目錄。
所以,我們提取文件系統(tǒng)主要是在/out/target/product/generic 目錄下,我們可以看到里面有obj 目錄,進(jìn)入obj 目錄看看,里面是android 文件系統(tǒng)非常重要的內(nèi)容:
/obj
APPS (文件系統(tǒng)下/system/apps 目錄下的各種應(yīng)用程序)
SHARED_LIBRARIES (存放所有動(dòng)態(tài)庫)
STATIC_LIBRARIES(存放所有靜態(tài)庫)
EXECUTABLES (存放各種可執(zhí)行文件)
Linux 內(nèi)核啟動(dòng)掛載android根文件系統(tǒng)過程分析
順便羅列一下內(nèi)核啟動(dòng)流程:
/arch/arm/boot/compressed/head.S:
Start:
Decompressed_kernel() //在/arch/arm/boot/compressed/misc.c 中
Call_kernel()
Stext:
/init/main.c
Start_kernel()
Setup_arch()
…
Rest_init()
Init()
Do_basic_setup()
Prepare_namespace()
看到了這里,我已激動(dòng)得說不出話了,因?yàn)閬淼轿遗c掛載根文件系統(tǒng)最重要的接口函數(shù)。
/* This is a non __init function. Force it to be noinline otherwise gcc
* makes it inline to init() and it becomes part of init.text section
*/
static int noinline init_post(void)
{
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
current->signal->flags |= SIGNAL_UNKILLABLE;
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);
}
/*
* We try each of these until one succeeds. *
* The Bourne shell can be used instead of init if we are*
* trying to recover a really broken machine.*/
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting ""defaults...\n",
execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
其中,我們看到行代碼run_init_process(execute_command);
execute_command 是從UBOOT 傳遞過來的參數(shù),一般為/init,也就是調(diào)用文件系統(tǒng)里的init 初始化進(jìn)程。如果找不到init 文件就會(huì)在
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
中找,否則報(bào)錯(cuò)。
在這里由于我們的根文件系統(tǒng)是從/linuxrc 開始的,所以我硬性把它改為
if (execute_command) {
run_init_process("/linuxrc");
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
Android 文件系統(tǒng)初始化核心Init.c文件分析
上面我們說的init 這個(gè)文件是由android 源代碼編譯來的,編譯后在/out/target/product/generic/root/
其源碼在/system/core/init/init.c
Init.c 主要功能:
(1)安裝SIGCHLD 信號(hào)。(如果父進(jìn)程不等待子進(jìn)程結(jié)束,子進(jìn)程將成為僵尸進(jìn)程(zombie)從而占用系統(tǒng)資源。因此需要對SIGCHLD 信號(hào)做出處理,回收僵尸進(jìn)程的資源,避免造成不必要的資源浪費(fèi)。)
(2)對umask 進(jìn)行清零。
何為umask,請看http://www.szstudy.cn/showArticle/53978.shtml
(3)為rootfs 建立必要的文件夾,并掛載適當(dāng)?shù)姆謪^(qū)。
/dev (tmpfs)
/dev/pts (devpts)
/dev/socket
/proc (proc)
/sys (sysfs)
(4)創(chuàng)建/dev/null 和/dev/kmsg 節(jié)點(diǎn)。
(5)解析/init.rc,將所有服務(wù)和操作信息加入鏈表。
(6)從/proc/cmdline 中提取信息內(nèi)核啟動(dòng)參數(shù),并保存到全局變量。
(7)先從上一步獲得的全局變量中獲取信息硬件信息和版本號(hào),如果沒有則從/proc/cpuinfo 中提取,并保存到全局變量。
(8)根據(jù)硬件信息選擇一個(gè)/init.(硬件).rc,并解析,將服務(wù)和操作信息加入鏈表。
在G1 的ramdisk 根目錄下有兩個(gè)/init.(硬件).rc:init.goldfish.rc 和init.trout.rc,init 程序會(huì)根據(jù)上一步獲得的硬件信息選擇一個(gè)解析。
(9)執(zhí)行鏈表中帶有“early-init”觸發(fā)的的命令。
(10)遍歷/sys 文件夾,是內(nèi)核產(chǎn)生設(shè)備添加事件(為了自動(dòng)產(chǎn)生設(shè)備節(jié)點(diǎn))。
(11)初始化屬性系統(tǒng),并導(dǎo)入初始化屬性文件。
(12)從屬性系統(tǒng)中得到ro.debuggable,若為1,則初始化keychord 監(jiān)聽。
(13)打開console,如果cmdline 中沒有指定console 則打開默認(rèn)的 /dev/console
(14)讀取/initlogo.rle(一張565 rle 壓縮的位圖),如果成功則在
/dev/graphics/fb0 顯示Logo,如果失敗則將/dev/tty0 設(shè)為TEXT 模式并打開/dev/tty0,輸出文“ANDROID”字樣。
(15)判斷cmdline 中的參數(shù),并設(shè)置屬性系統(tǒng)中的參數(shù):
1、 如果 bootmode 為
- factory,設(shè)置ro.factorytest 值為1
- factory2,設(shè)置ro.factorytest 值為2
- 其他的設(shè)ro.factorytest 值為0
2、如果有serialno 參數(shù),則設(shè)置ro.serialno,否則為""
3、如果有bootmod 參數(shù),則設(shè)置ro.bootmod,否則為"unknown"
4、如果有baseband 參數(shù),則設(shè)置ro.baseband,否則為"unknown"
5、如果有carrier 參數(shù),則設(shè)置ro.carrier,否則為"unknown"
6、如果有bootloader 參數(shù),則設(shè)置ro.bootloader,否則為"unknown"
7、通過全局變量(前面從/proc/cpuinfo 中提取的)設(shè)置ro.hardware 和
ro.version。
(16)執(zhí)行所有觸發(fā)標(biāo)識(shí)為init 的action。
(17)開始property 服務(wù),讀取一些property 文件,這一動(dòng)作必須在前面
那些ro.foo 設(shè)置后做,以便/data/local.prop 不能干預(yù)到他們。
- /system/build.prop
- /system/default.prop
- /data/local.prop
- 在讀取默認(rèn)的 property 后讀取 presistent propertie,在 /data/property 中
(18)為 sigchld handler 創(chuàng)建信號(hào)機(jī)制
(19)確認(rèn)所有初始化工作完成:
device_fd(device init 完成)
property_set_fd(property server start 完成)
signal_recv_fd (信號(hào)機(jī)制建立)
(20) 執(zhí)行所有觸發(fā)標(biāo)識(shí)為early-boot 的action
(21) 執(zhí)行所有觸發(fā)標(biāo)識(shí)為boot 的action
(22)基于當(dāng)前property 狀態(tài),執(zhí)行所有觸發(fā)標(biāo)識(shí)為property 的action
(23)注冊輪詢事件:
- device_fd
- property_set_fd
-signal_recv_fd
-如果有keychord,則注冊keychord_fd
(24)如果支持BOOTCHART,則初始化BOOTCHART
(25)進(jìn)入主進(jìn)程循環(huán):
- 重置輪詢事件的接受狀態(tài),revents 為0
- 查詢action 隊(duì)列,并執(zhí)行。
- 重啟需要重啟的服務(wù)
- 輪詢注冊的事件
- 如果signal_recv_fd 的revents 為POLLIN,則得到一個(gè)信號(hào),獲取并處
理
- 如果device_fd 的revents 為POLLIN,調(diào)用handle_device_fd
- 如果property_fd 的revents 為POLLIN,調(diào)用handle_property_set_fd
- 如果keychord_fd 的revents 為POLLIN,調(diào)用handle_keychord
到了這里,整個(gè)android 文件系統(tǒng)已經(jīng)起來了。
初始化核心的核心init.rc文件分析
在上面紅色那一行(5)解析/init.rc,將所有 服務(wù)和操作信息加入鏈表。
parse_config_file("/init.rc");//在init.c 中代碼 (有關(guān) /init.rc的腳本我就不貼出來了)
名詞解 釋:
Android 初始化語言由四大類聲明組成:行為類(Actions)、命令類(Commands)、服務(wù)類(Services)、選項(xiàng)類(Options)。
初始化語言以行為單位,由以空格間隔的語言符號(hào)組成。C 風(fēng)格的反斜杠轉(zhuǎn)義符可以用來插入空白到語言符號(hào)。雙引號(hào)也可以用來防止文本被空格分成多個(gè)語言符號(hào)。當(dāng)反斜杠在行末時(shí),作為換行符。
* 以#開始(前面允許空格)的行為注釋。
* Actions 和Services 隱含聲明一個(gè)新的段落。所有該段落下Commands 或 Options 的聲明屬于該段落。第一段落前的Commands 或Options 被忽略。
* Actions 和Services 擁有唯一的命名。在他們之后聲明相同命名的類將被當(dāng)作錯(cuò)誤并忽略。
Actions 是一系列命令的命名。Actions 擁有一個(gè)觸發(fā)器(trigger)用來決定action 何時(shí)執(zhí)行。當(dāng)一個(gè)action 在符合觸發(fā)條件被執(zhí)行時(shí),如果它還沒被加入到待執(zhí)行隊(duì)列中的話,則加入到隊(duì)列最后。隊(duì)列中的action 依次執(zhí)行,action 中的命令也依次執(zhí)行。
Init 在執(zhí)行命令的中間處理其他活動(dòng)(設(shè)備創(chuàng)建/銷毀,property 設(shè)置,進(jìn)程重啟)。
Actions 的表現(xiàn)形式:
on <trigger>
<command>
<command>
<command>
重要的數(shù)據(jù)結(jié)構(gòu)兩個(gè)列表,一個(gè)隊(duì)列。
static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);
*.rc 腳本中所有 service 關(guān)鍵字定義的服務(wù)將會(huì)添加到 service_list 列表中。
*.rc 腳本中所有 on 關(guān)鍵開頭的項(xiàng)將會(huì)被會(huì)添加到 action_list 列表中。每個(gè)action 列表項(xiàng)都有一個(gè)列表,此列表用來保存該段落下的 Commands。
腳本解 析過程:
parse_config_file("/init.rc")
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
static void parse_config(const char *fn, char *s) {
...
case T_NEWLINE:
if (nargs) {
int kw = lookup_keyword(args[0]);
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
...
}
parse_config 會(huì)逐行對腳本進(jìn)行解析,如果關(guān)鍵字類型為 SECTION ,那么將會(huì)執(zhí)行parse_new_section();
類型為 SECTION 的關(guān)鍵字有: on 和 sevice
關(guān)鍵字 類型定義在 Parser.c (system\core\init) 文件中
Parser.c (system\core\init)
#define SECTION 0x01
#define COMMAND 0x02
#define OPTION 0x04
關(guān)鍵字 屬性
capability, OPTION, 0, 0)
class, OPTION, 0, 0)
class_start, COMMAND, 1, do_class_start)
class_stop, COMMAND, 1, do_class_stop)
console, OPTION, 0, 0)
critical, OPTION, 0, 0)
disabled, OPTION, 0, 0)
domainname, COMMAND, 1, do_domainname)
exec, COMMAND, 1, do_exec)
export, COMMAND, 2, do_export)
group, OPTION, 0, 0)
hostname, COMMAND, 1, do_hostname)
ifup, COMMAND, 1, do_ifup)
insmod, COMMAND, 1, do_insmod)
import, COMMAND, 1, do_import)
keycodes, OPTION, 0, 0)
mkdir, COMMAND, 1, do_mkdir)
mount, COMMAND, 3, do_mount)
on, SECTION, 0, 0)
oneshot, OPTION, 0, 0)
onrestart, OPTION, 0, 0)
restart, COMMAND, 1, do_restart)
service, SECTION, 0, 0)
setenv, OPTION, 2, 0)
setkey, COMMAND, 0, do_setkey)
setprop, COMMAND, 2, do_setprop)
setrlimit, COMMAND, 3, do_setrlimit)
socket, OPTION, 0, 0)
start, COMMAND, 1, do_start)
stop, COMMAND, 1, do_stop)
trigger, COMMAND, 1, do_trigger)
symlink, COMMAND, 1, do_symlink)
sysclktz, COMMAND, 1, do_sysclktz)
user, OPTION, 0, 0)
write, COMMAND, 2, do_write)
chown, COMMAND, 2, do_chown)
chmod, COMMAND, 2, do_chmod)
loglevel, COMMAND, 1, do_loglevel)
device, COMMAND, 4, do_device)
parse_new_section() 中再分別對 service 或者 on 關(guān)鍵字開頭的內(nèi)容進(jìn)行解
析。
...
case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
case K_on:
state->context = parse_action(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_action;
return;
}
break;
...
對 on 關(guān)鍵字開頭的內(nèi)容進(jìn)行解析
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
...
act = calloc(1, sizeof(*act));
act->name = args[1];
list_init(&act->commands);
list_add_tail(&action_list, &act->alist);
...
}
對 service 關(guān)鍵字開頭的內(nèi)容進(jìn)行解析
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc;
if (nargs < 3) {
parse_error(state, "services must have a name and a program\n");
return 0;
}
if (!valid_name(args[1])) {
parse_error(state, "invalid service name '%s'\n", args[1]);
return 0;
}
//如果服務(wù)已經(jīng)存在service_list 列表中將會(huì)被忽略
svc = service_find_by_name(args[1]);
if (svc) {
parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
return 0;
}
nargs -= 2;
svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
if (!svc) {
parse_error(state, "out of memory\n");
return 0;
}
svc->name = args[1];
svc->classname = "default";
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
svc->args[nargs] = 0;
svc->nargs = nargs;
svc->onrestart.name = "onrestart";
list_init(&svc->onrestart.commands);
//添加該服務(wù)到 service_list 列表
list_add_tail(&service_list, &svc->slist);
return svc;
}
服務(wù)的 表現(xiàn)形式:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
申請一個(gè)service 結(jié)構(gòu)體,然后掛接到service_list 鏈表上,name 為服務(wù)的名稱,pathname 為執(zhí)行的命令,argument為命令的參數(shù)。之后的 option 用來控制這個(gè)service 結(jié)構(gòu)體的屬性,parse_line_service 會(huì)對 service 關(guān)鍵字后的內(nèi)容進(jìn)行解析并填充到 service 結(jié)構(gòu)中 ,當(dāng)遇到下一個(gè)service 或者on 關(guān)鍵字的時(shí)候此service 選項(xiàng)解析結(jié)束。
例如:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
服務(wù)名稱為: zygote
啟動(dòng)該服務(wù)執(zhí)行的命令: /system/bin/app_process
命令的參數(shù): -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666: 創(chuàng)建一個(gè)名為:/dev/socket/zygote 的 socket ,
類型為:stream
養(yǎng)成勵(lì)志的習(xí)慣,一生勵(lì)志——文指星終極勵(lì)志
經(jīng)典英語口語與英語學(xué)習(xí)資料
15本經(jīng)典C、C++、MFC、VC++教程,都是pdf完整版的
30本經(jīng)典Linux學(xué)習(xí)和開發(fā)教程和資料,都是pdf完整版的
10本經(jīng)典Java教程,都是pdf完整版的
15本超級(jí)經(jīng)典Android教程,都是pdf完整版的
一語驚人英語口語--經(jīng)典口語