最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Kernel怎么跳轉(zhuǎn)到Android:linux與安卓的交界

2023-07-07 09:31 作者:TrustZone0937  | 我要投稿


上一篇寫了Uboot怎么到Linux kernel,這一章來看看linux kernel怎么到Android的。

雖然是零零碎碎的學習了一些關于Linux的知識,但是對于這個部分基本上沒有站在系統(tǒng)的角度去看過。

1、前言

kernel的啟動主要分為兩個階段。

1、階段一

從入口跳轉(zhuǎn)到start_kernel之前的階段。

對應代碼arch/arm/kernel/head.S中stext的實現(xiàn):

ENTRY(stext)

  • 這個階段主要由匯編語言實現(xiàn)。

  • 這個階段主要負責MMU打開之前的一些操作,以及打開MMU的操作。

  • 由于這個階段MMU還沒有打開,并且kernel加載地址和連接地址并一致,所以需要使用位置無關設計。在運行過程中運行地址和加載地址一致(如果不明白的話建議先參考一下《[kernel 啟動流程] 前篇——vmlinux.lds分析》)。

(上一篇從uboot到kernel的地方,講了kernel啟動后的幾個階段,停在start_kernel部分)

2、階段二

start_kernel開始的階段。

2、正題-kernel-uboot

Android生在linux內(nèi)核基礎上,linux內(nèi)核啟動的最后一步,一定是啟動的android的進程。

然后我們也知道了內(nèi)核啟動分為三個階段,第一二是運行head.S文件和head-common.S,第三個階段是允許第二是運行main.c文件。

對于ARM的處理器,內(nèi)核第一個啟動的文件是arc/arm/kernel下面的head.S文件。、

當然arc/arm/boot/compress下面 也有這個文件,這個文件和上面的文件略有不同,當要生成壓縮的內(nèi)核時zImage時,啟動的是后者,后者與前者不同的時,它前面的代碼是做自解壓的,后面 的代碼都相同。

我們這里這分析arc/arm/kernel下面的head.S文件。當head.S所作的工作完成后它會跳到init/目錄下跌的 main.c的start_kernel函數(shù)開始執(zhí)行。

因為我們要研究的是過渡階段,而不是整個啟動流程。(后面會研究的。)這里直接看第三個--start_kernel階段。

asmlinkage?void?__init?start_kernel(void)??
{??
???????…………………….??
???????……………………..??
???????printk(KERN_NOTICE);??
???????printk(linux_banner);??
???????setup_arch(&command_line);??
???????setup_command_line(command_line);??


???????parse_early_param();??
???????parse_args("Booting?kernel",static_command_line,?__start___param,??
????????????????__stop___param?-?__start___param,??
????????????????&unknown_bootoption);??
……………………??
…………………………????????
???????init_IRQ();??
???????pidhash_init();??
???????init_timers();??
???????hrtimers_init();??
???????softirq_init();??
???????timekeeping_init();??
???????time_init();??
???????profile_init();??
…………………………??
……………………………??
???????console_init();??
………………………………??
………………………………??
???????rest_init();??
}??

從上面可以看出start_kernel首先是打印內(nèi)核信息,然后對bootloader傳進來的一些參數(shù)進行處理,再接著執(zhí)行各種各樣的初始化,在這其中會初始化控制臺。最后會調(diào)用rest_init();

我們再來看rest_init()函數(shù)

static?void?noinline?__init_refok?rest_init(void)??
????__releases(kernel_lock)??
{??
????int?pid;??

????kernel_thread(kernel_init,?NULL,?CLONE_FS?|?CLONE_SIGHAND);??
????............??????
}

他啟動了kernel_init這個函數(shù),再來看kerne_init函數(shù)

static?int?__init?kernel_init(void?*?unused)??
{??
????..............................??

????if?(!ramdisk_execute_command)??
????????ramdisk_execute_command?=?"/init";??

????if?(sys_access((const?char?__user?*)?ramdisk_execute_command,?0)?!=?0)?{??
????????ramdisk_execute_command?=?NULL;??
????????prepare_namespace();??
????}??

????/*??
?????*?Ok,?we?have?completed?the?initial?bootup,?and??
?????*?we're?essentially?up?and?running.?Get?rid?of?the??
?????*?initmem?segments?and?start?the?user-mode?stuff..??
?????*/
??
????init_post();??
????return?0;??
}??

kernel_init先調(diào)用了prepare_namespace();然后調(diào)用了init_post函數(shù)

void?__init?prepare_namespace(void)??
{??
????..........................??
????mount_root();??
????.....................??
}??

可以看出prepare_namespace調(diào)用了mount_root掛接根文件系統(tǒng)。接著kernel_init再執(zhí)行init_post

static?int?noinline?init_post(void)??
{??
????.......................................??
????/*打開dev/console控制臺,并設置為標準輸入、輸出*/??

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

????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.??
?????*/
??

????//如果bootloader指定了init參數(shù),則啟動init參數(shù)指定的進程??
????if?(execute_command)?{??
????????run_init_process(execute_command);??
????????printk(KERN_WARNING?"Failed?to?execute?%s.??Attempting?"??
????????????????????"defaults...\n",?execute_command);??
????}??

????//如果沒有指定init參數(shù),則分別帶sbin、etc、bin目錄下啟動init進程??
????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的會等待init進程返回才往后面執(zhí)行,所有它一旦找到一個init可執(zhí)行的文件它將一去不復返。

綜上,內(nèi)核啟動的過程大致為以下幾步:

  • 1.檢查CPU和機器類型

  • 2.進行堆棧、MMU等其他程序運行關鍵的東西進行初始化

  • 3.打印內(nèi)核信息

  • 4.執(zhí)行各種模塊的初始化

  • 5.掛接根文件系統(tǒng)

  • 6.啟動第一個init進程

  • 7.android啟動

說明一

總結(jié)一個圖:kernel 到android核心啟動過程

在這里插入圖片描述


kernel鏡像執(zhí)行跳轉(zhuǎn)到start_kernel開始執(zhí)行,在rest_init會創(chuàng)建兩個kernel 進程(線程),其分別是為kernel_init 與kthreadd,創(chuàng)建完后系統(tǒng)通過init_idle_bootup_task蛻化為idle進程(cpu_idle)。


在這里插入圖片描述


調(diào)用kernel_thread()創(chuàng)建1號內(nèi)核線程, 該線程隨后轉(zhuǎn)向用戶空間, 演變?yōu)閕nit進程


調(diào)用kernel_thread()創(chuàng)建kthreadd內(nèi)核線程。

init_idle_bootup_task():當前0號進程init_task最終會退化成idle進程,所以這里調(diào)用init_idle_bootup_task()函數(shù),讓init_task進程隸屬到idle調(diào)度類中。即選擇idle的調(diào)度相關函數(shù)。
調(diào)用cpu_idle(),0號線程進入idle函數(shù)的循環(huán),在該循環(huán)中會周期性地檢查

kernel_init 中會執(zhí)行/init(ramdisk_execute_command的值為"/init")

在這里插入圖片描述

/init 啟動后執(zhí)行/system/core/init/main.cpp 中main 方法,這里執(zhí)行FirstStageMain()

在這里插入圖片描述


(看看這到了哪里?這到了咱們的的AVB那個地方啊)


FirstStageMain()中通過execv 執(zhí)行/system/bin/init,參數(shù)為selinux_setup。這里init 跟/init 一樣,因此再次執(zhí)行init 鏡像。這里如果是重啟到bootloader,會執(zhí)行InstallRebootSignalHandlers

在這里插入圖片描述


SetupSelinux 中再次執(zhí)行init,這里會注冊信號處理函數(shù)


從而參數(shù)second_stage,執(zhí)行SecondStageMain ,在這里解析.rc ,啟動ueventd,并等待其啟動完成。

在這里插入圖片描述


init 鏡像通過execv會執(zhí)行兩次,分別通過FirstStageMain和SecondStageMain執(zhí)行。


在這里插入圖片描述
在這里插入圖片描述


Zygote是Android系統(tǒng)創(chuàng)建新進程的核心進程,負責啟動Dalvik虛擬機,加載一些必要的系統(tǒng)資源和系統(tǒng)類,啟動system_server進程,隨后進入等待處理app應用請求。到這里我們就暫時停下,別走遠了。


說明二

總結(jié)一下整個流程

  • 第一步:手機開機后,引導芯片啟動,引導芯片開始從固化在ROM里的預設代碼執(zhí)行,加載引導程序到到RAM,bootloader檢查RAM,初始化硬件參數(shù)等功能;

  • 第二步:硬件等參數(shù)初始化完成后,進入到Kernel層,Kernel層主要加載一些硬件設備驅(qū)動,初始化進程管理等操作。在Kernel中首先啟動swapper進程(pid=0),用于初始化進程管理、內(nèi)管管理、加載Driver等操作,再啟動kthread進程(pid=2),這些linux系統(tǒng)的內(nèi)核進程,kthread是所有內(nèi)核進程的鼻祖;

  • 第三步:Kernel層加載完畢后,硬件設備驅(qū)動與HAL層進行交互。初始化進程管理等操作會啟動INIT進程 ,這些在Native層中;

  • 第四步:init進程(pid=1,init進程是所有進程的鼻祖,第一個啟動)啟動后,會啟動adbd,logd等用戶守護進程,并且會啟動servicemanager(binder服務管家)等重要服務,同時孵化出zygote進程,這里屬于C++ Framework,代碼為C++程序;

  • 第五步:zygote進程是由init進程解析init.rc文件后fork生成,它會加載虛擬機,啟動System Server(zygote孵化的第一個進程);System Server負責啟動和管理整個Java Framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服務;

  • 第六步:zygote同時會啟動相關的APP進程,它啟動的第一個APP進程為Launcher,然后啟動Email,SMS等進程,所有的APP進程都由zygote fork生成。

那么到這里我們就把整個系統(tǒng)的啟動串聯(lián)起來了從bootrom-bootloader-kernel。當然真實的系統(tǒng)為了安全,比如說基于ARM框架的,那肯定不止這些步驟,但是大體上也是穿插在這個流程之中的。

這個bridge系列真的蠻有意思,持續(xù)做下去。感謝前輩們的優(yōu)秀blog。

參考資料:

https://blog.csdn.net/yiranfeng/article/details/103549394
https://www.cnblogs.com/littleboy123/p/13208179.html
https://www.cnblogs.com/hzl6255/p/12142762.html
https://blog.csdn.net/lei7143/article/details/114269707
https://www.cnblogs.com/linucos/archive/2012/05/21/2511619.html


Kernel怎么跳轉(zhuǎn)到Android:linux與安卓的交界的評論 (共 條)

分享到微博請遵守國家法律
基隆市| 太康县| 镇康县| 秀山| 平湖市| 博野县| 曲阳县| 陵川县| 临桂县| 开远市| 崇信县| 嵊州市| 东光县| 诸城市| 延长县| 萨迦县| 南华县| 若尔盖县| 达拉特旗| 庆阳市| 安达市| 大埔县| 高要市| 吴桥县| 尼勒克县| 宜州市| 靖远县| 二连浩特市| 彭水| 安福县| 航空| 库尔勒市| 类乌齐县| 杭州市| 保亭| 山东| 阿巴嘎旗| 遂昌县| 田阳县| 和静县| 淅川县|