linux 核心啟動過程以及掛載android 根檔案系統的過程 ( 轉)

來源:互聯網
上載者:User

       主要介紹linux 核心啟動過程以及掛載android 根檔案系統的過程,以及介紹android 原始碼中檔案系統部分的淺析。

      主要原始碼目錄介紹
Makefile (全域的Makefile)
bionic (Bionic 含義為仿生,這裡面是一些基礎的庫的原始碼)
bootable (引導載入器)
build (build 目錄中的內容不是目標所用的代碼,而是編譯和配置所需要的指令碼和工具)
dalvik (JAVA 虛擬機器)
development (程式開發所需要的模板和工具)                                                    external (目標機器使用的一些庫)

frameworks (應用程式的架構層)
hardware (與硬體相關的庫)
packages (Android 的各種應用程式)
prebuilt (Android 在各種平台下編譯的預置指令碼)
recovery (與目標的恢複功能相關)
system (Android 的底層的一些庫)
out (編譯完成後產生的目錄,也就是我們移植檔案系統需要的目錄)

host 目錄的結構如下所示:
out/host/
|-- common
| `-- obj (JAVA 庫)
`-- linux-x86
|-- bin (二進位程式)
|-- framework (JAVA 庫,*.jar 檔案)
|-- lib (共用庫*.so)
`-- obj (中間產生的目標檔案)
host 目錄是一些在主機上用的工具,有一些是二進位程式,有一些是JAVA 的程式。

target 目錄的結構如下所示:
out/target/
|-- common
| |-- R (資源檔)
| |-- docs
| `-- obj (目標檔案)
`-- product
`-- generic
其中common 目錄表示通用的內容,product 中則是針對產品的內容。
在common 目錄的obj 中,包含兩個重要的目錄:
APPS 中包含了JAVA 應用程式產生的目標,每個應用程式對應其中一個子目錄,將結合每個應用程式的原始檔案產生Android 應用程式的APK 包。                                                                                       JAVA_LIBRARIES 中包含了JAVA 的庫,每個庫對應其中一個子目錄。

所以,我們提取檔案系統主要是在/out/target/product/generic 目錄下,我們可以看到裡面有obj 目錄,進入obj 目錄看看,裡面是android 檔案系統非常重要的內容:

/obj
APPS (檔案系統下/system/apps 目錄下的各種應用程式)
SHARED_LIBRARIES (存放所有動態庫)
STATIC_LIBRARIES(存放所有靜態庫)
EXECUTABLES (存放各種可執行檔)

 

Linux 核心啟動掛載android根檔案系統過程分析

順便羅列一下核心啟動流程:

/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()

看到了這裡,我已激動得說不出話了,因為來到我與掛載根檔案系統最重要的介面函數。

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);
}

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 傳遞過來的參數,一般為/init,也就是調用檔案系統裡的init 初始化進程。如果找不到init 檔案就會在
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
中找,否則報錯。

在這裡由於我們的根檔案系統是從/linuxrc 開始的,所以我硬性把它改為
if (execute_command) {
run_init_process("/linuxrc");
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}

Android 檔案系統初始化核心Init.c檔案分析

       上面我們說的init 這個檔案是由android 原始碼編譯來的,編譯後在/out/target/product/generic/root/

其源碼在/system/core/init/init.c

Init.c 主要功能:

(1)安裝SIGCHLD 訊號。(如果父進程不等待子進程結束,子進程將成為殭屍進程(zombie)從而佔用系統資源。因此需要對SIGCHLD 訊號做出處理,回收殭屍進程的資源,避免造成不必要的資源浪費。)
(2)對umask 進行清零。
        何為umask,請看http://www.szstudy.cn/showArticle/53978.shtml
(3)為rootfs 建立必要的檔案夾,並掛載適當的分區。
/dev (tmpfs)
/dev/pts (devpts)
/dev/socket
/proc (proc)
/sys (sysfs)
(4)建立/dev/null 和/dev/kmsg 節點。
(5)解析/init.rc,將所有服務和操作資訊加入鏈表。
(6)從/proc/cmdline 中提取資訊核心啟動參數,並儲存到全域變數。
(7)先從上一步獲得的全域變數中擷取資訊硬體資訊和版本號碼,如果沒有則從/proc/cpuinfo 中提取,並儲存到全域變數。
(8)根據硬體資訊選擇一個/init.(硬體).rc,並解析,將服務和操作資訊加入鏈表。
在G1 的ramdisk 根目錄下有兩個/init.(硬體).rc:init.goldfish.rc 和init.trout.rc,init 程式會根據上一步獲得的硬體資訊選擇一個解析。
(9)執行鏈表中帶有“early-init”觸發的的命令。
(10)遍曆/sys 檔案夾,是核心產生裝置添加事件(為了自動產生裝置節點)。
(11)初始化屬性系統,並匯入初始化屬性檔案。
(12)從屬性系統中得到ro.debuggable,若為1,則初始化keychord 監聽。
(13)打開console,如果cmdline 中沒有指定console 則打開默認的 /dev/console
(14)讀取/initlogo.rle(一張565 rle 壓縮的位圖),如果成功則在
/dev/graphics/fb0 顯示Logo,如果失敗則將/dev/tty0 設為TEXT 模式並打開/dev/tty0,輸出文“ANDROID”字樣。
(15)判斷cmdline 中的參數,並設定屬性系統中的參數:
1、 如果 bootmode 為
- factory,設置ro.factorytest 值為1
- factory2,設置ro.factorytest 值為2
- 其他的設ro.factorytest 值為0
2、如果有serialno 參數,則設置ro.serialno,否則為""
3、如果有bootmod 參數,則設置ro.bootmod,否則為"unknown"
4、如果有baseband 參數,則設置ro.baseband,否則為"unknown"
5、如果有carrier 參數,則設置ro.carrier,否則為"unknown"
6、如果有bootloader 參數,則設置ro.bootloader,否則為"unknown"
7、通過全域變數(前面從/proc/cpuinfo 中提取的)設置ro.hardware 和
ro.version。
(16)執行所有觸發標識為init 的action。
(17)開始property 服務,讀取一些property 檔案,這一動作必須在前面
那些ro.foo 設置後做,以便/data/local.prop 不能幹預到他們。
- /system/build.prop
- /system/default.prop
- /data/local.prop
- 在讀取默認的 property 後讀取 presistent propertie,在 /data/property 中
(18)為 sigchld handler 創建信號機制
(19)確認所有初始化工作完成:
device_fd(device init 完成)
property_set_fd(property server start 完成)
signal_recv_fd (信號機制建立)
(20) 執行所有觸發標識為early-boot 的action
(21) 執行所有觸發標識為boot 的action
(22)基於當前property 狀態,執行所有觸發標識為property 的action
(23)注冊輪詢事件:
- device_fd
- property_set_fd
-signal_recv_fd
-如果有keychord,則注冊keychord_fd
(24)如果支援BOOTCHART,則初始化BOOTCHART
(25)進入主進程循環:
- 重設輪詢事件的接受狀態,revents 為0
- 查詢action 隊列,並執行。
- 重啟需要重啟的服務
- 輪詢注冊的事件
- 如果signal_recv_fd 的revents 為POLLIN,則得到一個信號,獲取並處

- 如果device_fd 的revents 為POLLIN,調用handle_device_fd
- 如果property_fd 的revents 為POLLIN,調用handle_property_set_fd
- 如果keychord_fd 的revents 為POLLIN,調用handle_keychord
到了這裡,整個android 檔案系統已經起來了。

初始化核心的核心init.rc檔案分析

在上面紅色那一行(5)解析/init.rc,將所有服務和操作資訊加入鏈表。

       parse_config_file("/init.rc");//在init.c 中代碼 (有關 /init.rc的指令碼我就不貼出來了)

名詞解釋:
       Android 初始化語言由四大類聲明組成:行為類(Actions)、命令類(Commands)、服務類(Services)、選項類(Options)。
       初始化語言以行為單位,由以空格間隔的語言符號組成。C 風格的反斜線轉義符可以用來插入空白到語言符號。雙引號也可以用來防止文本被空格分成多個語言符號。當反斜線在行末時,作為分行符號。

       * 以#開始(前面允許空格)的行為注釋。
       * Actions 和Services 隱含聲明一個新的段落。所有該段落下Commands 或 Options 的聲明屬於該段落。第一段落前的Commands 或Options 被忽略。
       * Actions 和Services 擁有唯一的命名。在他們之後聲明相同命名的類將被當作錯誤並忽略。
          Actions 是一系列命令的命名。Actions 擁有一個觸發器(trigger)用來決定action 何時執行。當一個action 在符合觸發條件被執行時,如果它還沒被加入到待執行隊列中的話,則加入到隊列最後。隊列中的action 依次執行,action 中的命令也依次執行。

           Init 在執行命令的中間處理其他活動(裝置建立/銷毀,property 設定,進程重啟)。

          Actions 的表現形式:
          on <trigger>
          <command>
          <command>
          <command>

          重要的資料結構兩個列表,一個隊列。
static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);

         *.rc 指令碼中所有 service 關鍵字定義的服務將會添加到 service_list 列表中。
         *.rc 指令碼中所有 on 關鍵開頭的項將會被會添加到 action_list 列表中。每個action 清單項目都有一個列表,此列表用來儲存該段落下的 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)                                                                                   

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.