Android研究-linux内核启动到android系统
很多人閱讀代碼,總喜歡從頭開始,這樣覺得很安全,有依靠,無論如何總是能知道“頭”,有頭就能找到任何需要的部分。
Android生在linux內核基礎上,linux內核啟動的最后一步,一定是啟動的android的進程,下面看收集到的文章。
1. linux內核啟動過程
引用地址:http://blog.csdn.net/ayangke/article/details/6888699
?????? 內核版本:2.6.22? 為什么要采用這樣一個較低的版本進行移植了,因為韋東山大牛說了,低版本的才能學到東西,越是高版本需要移植時做的工作量越少,學的東西越少。
?????? 內核啟動分為三個階段,第一是運行head.S文件和head-common.S,第三個階段是允許第二是運行main.c文件
?????? 對于ARM的處理器,內核第一個啟動的文件是arc/arm/kernel下面的head.S文件。當然arc/arm/boot/compress下面 也有這個文件,這個文件和上面的文件略有不同,當要生成壓縮的內核時zImage時,啟動的是后者,后者與前者不同的時,它前面的代碼是做自解壓的,后面 的代碼都相同。我們這里這分析arc/arm/kernel下面的head.S文件。當head.S所作的工作完成后它會跳到init/目錄下跌的 main.c的start_kernel函數開始執行。
?
第一階段:
?
首先截取部分head.S文件
ENTRY(stext)
?????? msr? cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
????????????????????????????????????????? @ andirqs disabled
?????? mrc? p15,0, r9, c0, c0?????????? @ get processor id
?????? bl??? __lookup_processor_type???????????? @ r5=procinfo r9=cpuid
?????? movs?????? r10,r5???????????????????????? @ invalidprocessor (r5=0)?
?????? beq? __error_p???????????????????? @ yes, error 'p'
?????? bl??? __lookup_machine_type??????? @ r5=machinfo
?????? movs?????? r8,r5?????????????????????????? @ invalidmachine (r5=0)?
?????? beq? __error_a???????????????????? @ yes, error 'a'
?????? bl??? __create_page_tables
?
?????? /*
?????? ?*The following calls CPU specific code in a position independent
?????? ?*manner.? See arch/arm/mm/proc-*.S fordetails.? r10 = base of
?????? ?*xxx_proc_info structure selected by __lookup_machine_type
?????? ?*above.? On return, the CPU will be readyfor the MMU to be
?????? ?*turned on, and r0 will hold the CPU control register value.
?????? ?*/
?????? ldr?? r13,__switch_data??????? @ address to jump toafter
????????????????????????????????????????? @ mmuhas been enabled
?????? adr?? lr,__enable_mmu????????? @ return (PIC)address
?
?
第一步,執行的是__lookup_processor_type,這個函數是檢查處理器型號,它讀取你的電路板的CPU型號與內核支持的處理器進行比較看是否能夠處理。這個我們不關心它的具體實現過程,因為現在主流處理器內核都提供了支持。
?????? 第二步,執行的是__lookup_machine_type,這個函數是來檢查機器型號的,它會讀取你bootloader傳進來的機器ID和他能夠處 理的機器ID進行比較看是否能夠處理。內核的ID號定義在arc/arm/tool/mach_types文件中MACH_TYPE_xxxx宏定義。內 核究竟就如何檢查是否是它支持的機器的呢?實際上每個機器都會在/arc/arm/mach-xxxx/smdk-xxxx.c文件中有個描述特定機器的 數據結構,如下
?
[html] view plaincopy
MACHINE_START和 MACHINE_END實際上被展開成一個結構體
[html] view plaincopy
?
于是上面的數據結構就被展開為
[html] view plaincopy
每個機器都會有一個machine_desc__mach_desc結構,內核通過檢查每個machine_desc__mach_desc的nr 號和bootloader傳上來的ID進行比較,如果相同,內核就認為支持該機器,而且內核在后面的工作中會調用該機器的 machine_desc__mach_desc_結構中的方法進行一些初始化工作。
?????? 第三步,創建一級頁表。
?????? 第四步,在R13中保存__switch_data 這個函數的地址,在第四步使能mmu完成后會跳到該函數執行。
第五步,執行的是__enable_mmu,它是使能MMU,這個函數調用了__turn_mmu_on函數,讓后在_turn_mmu_on在最后將第三步賦給R13的值傳給了PC指針 (mov??? pc, r13),于是內核開始跳到__switch_data這個函數開始執行。
?
我們再來看arch/arm/kenel/head-common.S這個文件中的__switch_data函數
?
[html] view plaincopy
?
這個函數做的工作是,復制數據段清楚BBS段,設置堆在指針,然后保存處理器內核和機器內核等工作,最后跳到start_kernel函數。于是內核開始執行第二階段。
?
第二階段:
?
?????? 我們再來看init/目錄下的main.c的start_kernel函數,這里我只截圖了部分。
[html] view plaincopy
從上面可以看出start_kernel首先是打印內核信息,然后對bootloader傳進來的一些參數進行處理,再接著執行各種各樣的初始化,在這其中會初始化控制臺。最后會調用rest_init();
我們再來看rest_init()函數
[html] view plaincopy?他啟動了kernel_init這個函數,再來看kerne_init函數
[html] view plaincopykernel_init先調用了prepare_namespace();然后調用了init_post函數
[html] view plaincopy可以看出prepare_namespace調用了mount_root掛接根文件系統。接著kernel_init再執行init_post
[html] view plaincopy?
注意上面的run_init_process的會等待init進程返回才往后面執行,所有它一旦找到一個init可執行的文件它將一去不復返。
綜上,內核啟動的過程大致為以下幾步:
1.檢查CPU和機器類型
2.進行堆棧、MMU等其他程序運行關鍵的東西進行初始化
3.打印內核信息
4.執行各種模塊的初始化
5.掛接根文件系統
6.啟動第一個init進程
2. android啟動
引用地址:http://monner.iteye.com/blog/728334
Android 系統啟動分析(轉)
init進程是Android啟動后系統執行的第一個名稱為init的可執行程序。這個程序以一個守護進程的方式運行,它提供了以下功能:- 設備管理
- 解析啟動腳本
- 執行啟動腳本中的基本功能
- 執行啟動腳本中的各種功能
1、init可執行程序
init 可執行文件是系統運行的第一個用戶空間程序,它以守護進程的方式運行。因此這個程序的init.c文件包含main函數的入口,基本分析如下: int main(int argc,char**argv){ ? ?(省略若干。。。) ? ?umask(0); ?/*對umask進行清零。*/ ? ? mkdir("/dev",0755);/*為rootfs建立必要的文件夾,并掛載適當的分區。 */ ? ? mkdir("/proc",0755); ? ? mkdir("/sys",0755);? ? mount("tmpfs","/dev","tmpfs",0,"mode=0755"); ? ? mkdir("/dev/pts",0755); ? ? 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和/dev/kmsg節點*/ ? ? open_devnull_stdio(); ? ? log_init(); ? ?/*解析/init.rc,將所有服務和操作信息加入鏈表。*/ ? ? INFO("reading config file\n"); ? ? parse_config_file("/init.rc");? ? /*獲取內核命令行參數*/ ? ? qemu_init(); ? ? import_kernel_cmdline(0); ? ? /*先從上一步獲得的全局變量中獲取信息硬件信息和版本號,如果沒有則從/proc/cpuinfo中提取,*并保存到全局變量。根據硬件信息選擇一個/init.(硬件).rc,并解析,將服務和操作信息加入鏈表。*/ ? ? get_hardware_name(); ? ? snprintf(tmp,sizeof(tmp),"/init.%s.rc", hardware); ? ? parse_config_file(tmp); ? ? /*執行鏈表中帶有“early-init”觸發的的命令。*/ ? ? action_for_each_trigger("early-init", action_add_queue_tail); ? ? drain_action_queue(); ? ? /*遍歷/sys文件夾,是內核產生設備添加事件(為了自動產生設備節點)。*初始化屬性系統,并導入初始化屬性文件。用于在系統運行過程中動態創建設備節點、刪除設備節點等操作*/ ? ? INFO("device init\n"); ? ? device_fd = device_init();? ? property_init(); ? ? // 從屬性系統中得到ro.debuggable,若為1,則初始化keychord監聽。 ? ? debuggable = property_get("ro.debuggable"); ? ? if(debuggable &&!strcmp(debuggable,"1")){ ? ? ? ? keychord_fd = open_keychord(); ? ? } ? ? /*打開console,如果cmdline中沒有指定的console則打開默認的/dev/console*/ ? ? if(console[0]){ ? ? ? ? snprintf(tmp,sizeof(tmp),"/dev/%s", console); ? ? ? ? console_name = strdup(tmp); ? ? }? ? fd = open(console_name, O_RDWR); ? ? if(fd >=0) ? ? ? ? have_console =1; ? ? close(fd); ? ? /*讀取/initlogo.rle(一張位圖),如果成功則在/dev/graphics/fb0 顯示Logo,如果失敗則將/dev/tty0*設為TEXT模式并打開/dev/tty0,輸出文本ANDROID(本人修改為Zhao Rui Jia做為啟動項目的修改)。*/ ? ? if( load_565rle_image(INIT_IMAGE_FILE)){ ? ? fd = open("/dev/tty0", O_WRONLY); ? ? if(fd >=0){ ? ? ? ? constchar*msg; ? ? ? ? ? ? msg ="\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ?// console is 40 cols x 30 lines ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? ? "\n" ? ? ? /*" ? ? ? ? ? ? A N D R O I D ";*/ ? ? ? ? " ? ? ? ? ?z h a o R u i J i a"; ? ? ? ? write(fd, msg, strlen(msg)); ? ? ? ? close(fd); ? ? } ? ? } ? ?/* 判斷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。*/ ? ? if(qemu[0]) ? ? ? ? import_kernel_cmdline(1); if(!strcmp(bootmode,"factory")) ? ? ? ? property_set("ro.factorytest","1"); ? ? elseif(!strcmp(bootmode,"factory2")) ? ? ? ? property_set("ro.factorytest","2"); ? ? else ? ? ? ? property_set("ro.factorytest","0");? ? property_set("ro.serialno", serialno[0]? serialno :""); ? ? property_set("ro.bootmode", bootmode[0]? bootmode :"unknown"); ? ? property_set("ro.baseband", baseband[0]? baseband :"unknown"); ? ? property_set("ro.carrier", carrier[0]? carrier :"unknown"); ? ? property_set("ro.bootloader", bootloader[0]? bootloader :"unknown");? ? property_set("ro.hardware", hardware); ? ? snprintf(tmp, PROP_VALUE_MAX,"%d", revision); ? ? property_set("ro.revision", tmp);? ? /*執行所有觸發標識為init的action。*/ ? ? action_for_each_trigger("init", action_add_queue_tail); ? ? drain_action_queue(); ? ? property_set_fd = start_property_service();? ? ?/* 為sigchld handler創建信號機制*/ ? ? if(socketpair(AF_UNIX, SOCK_STREAM,0, s)==0){ ? ? ? ? signal_fd = s[0]; ? ? ? ? signal_recv_fd = s[1]; ? ? ? ? fcntl(s[0], F_SETFD, FD_CLOEXEC); ? ? ? ? fcntl(s[0], F_SETFL, O_NONBLOCK); ? ? ? ? fcntl(s[1], F_SETFD, FD_CLOEXEC); ? ? ? ? fcntl(s[1], F_SETFL, O_NONBLOCK); ? ? }? ? /* 確認所有初始化工作完成* device_fd(device init 完成)* property_set_fd(property server start 完成)* signal_recv_fd (信號機制建立) */ ? ? if((device_fd <0)|| ? ? ? ? (property_set_fd <0)|| ? ? ? ? (signal_recv_fd <0)){ ? ? ? ? ERROR("init startup failure\n"); ? ? ? ? return1; ? ? }? ? /* execute all the boot actions to get us started */ ? ? action_for_each_trigger("early-boot", action_add_queue_tail); ? ? action_for_each_trigger("boot", action_add_queue_tail); ? ? drain_action_queue();? ? /* run all property triggers based on current state of the properties */ ? ? queue_all_property_triggers(); ? ? drain_action_queue();? ? /* enable property triggers */ ? property_triggers_enabled =1; ? ? /** ? ?注冊輪詢事件:* ? - device_fd* ? - property_set_fd* ? -signal_recv_fd* ? -如果有keychord,則注冊keychord_fd*/ ? ? ufds[0].fd = device_fd; ? ? ufds[0].events = POLLIN; ? ? ufds[1].fd = property_set_fd; ? ? ufds[1].events = POLLIN; ? ? ufds[2].fd = signal_recv_fd; ? ? ufds[2].events = POLLIN; ? ? fd_count =3;? ? if(keychord_fd >0){ ? ? ? ? ufds[3].fd = keychord_fd; ? ? ? ? ufds[3].events = POLLIN; ? ? ? ? fd_count++; ? ? }else{ ? ? ? ? ufds[3].events =0; ? ? ? ? ufds[3].revents =0; ? ? }/*如果支持BOOTCHART,則初始化BOOTCHART*/#if BOOTCHART ? ? bootchart_count = bootchart_init(); ? ? if(bootchart_count <0){ ? ? ? ? ERROR("bootcharting init failure\n"); ? ? }elseif(bootchart_count >0){ ? ? ? ? NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); ? ? }else{ ? ? ? ? NOTICE("bootcharting ignored\n"); ? ? }#endif ? /* ?*進入主進程循環:* ?- 重置輪詢事件的接受狀態,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*/ for(;;){ ? ? ? ? int nr, i, timeout =-1;? ? ? ? for(i =0; i < fd_count; i++) ? ? ? ? ? ? ufds[i].revents =0;? ? ? ? drain_action_queue(); ? ? ? ? restart_processes();? ? ? ? if(process_needs_restart){ ? ? ? ? ? ? timeout =(process_needs_restart - gettime())*1000; ? ? ? ? ? ? if(timeout <0) ? ? ? ? ? ? ? ? timeout =0; ? ? ? ? }#if BOOTCHART ? ? ? ? if(bootchart_count >0){ ? ? ? ? ? ? if(timeout <0|| timeout > BOOTCHART_POLLING_MS) ? ? ? ? ? ? ? ? timeout = BOOTCHART_POLLING_MS; ? ? ? ? ? ? if(bootchart_step()<0||--bootchart_count ==0){ ? ? ? ? ? ? ? ? bootchart_finish(); ? ? ? ? ? ? ? ? bootchart_count =0; ? ? ? ? ? ? } ? ? ? ? }#endif ? ? ? ? nr = poll(ufds, fd_count, timeout); ? ? ? ? if(nr <=0) ? ? ? ? ? ? continue;? ? ? ? if(ufds[2].revents == POLLIN){ ? ? ? ? ? ? /* we got a SIGCHLD - reap and restart as needed */ ? ? ? ? ? ? read(signal_recv_fd, tmp,sizeof(tmp)); ? ? ? ? ? ? while(!wait_for_one_process(0)) ? ? ? ? ? ? ? ? ; ? ? ? ? ? ? continue; ? ? ? ? }? ? ? ? if(ufds[0].revents == POLLIN) ? ? ? ? ? ? handle_device_fd(device_fd);? ? ? ? if(ufds[1].revents == POLLIN) ? ? ? ? ? ? handle_property_set_fd(property_set_fd); ? ? ? ? if(ufds[3].revents == POLLIN) ? ? ? ? ? ? handle_keychord(keychord_fd); ? ? }? ? return0;}2、啟動腳本init.rc
在 Android中使用啟動腳本init.rc,可以在系統的初始化過程中進行一些簡單的初始化操作。這個腳本被直接安裝到目標系統的根文件系統中,被 init可執行程序解析。 init.rc是在init啟動后被執行的啟動腳本,其余發主要包含了以下內容:
- Commands:命令
- Actions:動作
- Triggers:觸發條件
- Services:服務
- Options:選項
- Propertise:屬性
Commands是一些基本的操作,例如:
? ? mkdir /sdcard 0000 system systemmkdir /systemmkdir /data 0771 system systemmkdir /cache 0770 system cachemkdir /config 0500 root rootmkdir /sqlite_stmt_journals 01777 root rootmount tmpfs tmpfs /sqlite_stmt_journals size=4m這些命令在init可執行程序中被解析,然后調用相關的函數來實現。 Actions(動作)表示一系列的命令,通常在Triggers(觸發條件)中調用,動作和觸發條件例如:
? ? on initexport PATH /sbin:/system/sbin:/system/bin:/system/xbininit表示一個觸發條件,這個觸發事件發生后,進行設置環境變量和建立目錄的操作稱為一個“動作” Services(服務)通常表示啟動一個可執行程序,Options(選項)是服務的附加內容,用于配合服務使用。
service vold /system/bin/voldsocket vold stream 0660 root mountservice bootsound /system/bin/playmp3user mediagroup audiooneshotvold和bootsound分別是兩個服務的名稱,/system/bin /vold和/system /bin/playmp3分別是他們所對應的可執行程序。socket、user、group、oneshot就是配合服務使用的選項。 Properties(屬性)是系統中使用的一些值,可以進行設置和讀取。
? ? setprop ro.FOREGROUND_APP_MEM 1536 ? ? setprop ro.VISIBLE_APP_MEM 2048 ? ? start adbdsetprop 用于設置屬性,on property可以用于判斷屬性,這里的屬性在整個Android系統運行中都是一致的。
綜上如果想要修改啟動過程只需要修改init.c或者init.rc里的內容即可.
3. 總結:
(1)內核的init_post類似接口,會去文件系統中啟動init類似的用戶進程
(2)android實現了這樣的init,這就是android框架啟動的地方,當然linux內核也可說是android系統的一部分
(3)init進程無限分裂,啟動框架,演變成android系統
(4)android的init進程的代碼在system/core/init/init.c中,從main函數開始.
?
本文完~
總結
以上是生活随笔為你收集整理的Android研究-linux内核启动到android系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux系统调用-- mount/um
- 下一篇: Linux zip 加密压缩