編輯:關於Android編程
(懶人最近想起我還有csdn好久沒打理了,這個android init躺在我的草稿箱中快5年了,稍微改改發出來吧)
android設備上電,引導程序引導進入boot(通常是uboot),加載initramfs、kernel鏡像,啟動kernel後,進入用戶態程序。第一個用戶空間程序是init, PID固定是1.在android系統上,init的代碼位於/system/core/init下,基本功能有:
管理設備解析並處理啟動腳本init.rc實時維護這個init.rc中的服務init進程的系統初始化和服務流程,可以簡單的看下init.c中的main函數。這裡簡要分析一下這個函數的主要作用,細節不展開或後繼再展開。
簡要說下main函數的各項操作吧:
if (!strcmp(basename(argv[0]), ueventd)) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), watchdogd)) return watchdogd_main(argc, argv);
以上這段,是main函數入口的第一句,這是判斷是跑那個程序。
/system/core/init 這裡面的一份代碼,編出的二進制可執行程序init,實際是分為三個程序運行的,指示大家共享一份代碼而已。三份分別是:
/init: 實際init可執行程序
/sbin/ueventd:ueventd daemon程序,是init的軟鏈接
/sbin/watchdogd: watchdogd daemon程序,也是init的軟鏈接。
我們都知道,main函數的參數argv的第一個,argv[0]為自身運行目錄路徑和程序名,這裡就根據這個條件來判斷代碼走的路徑。為何這樣做?其實完全可以再另外寫一個ueventd或watchdogd的一套程序,定義main函數啊。其實這裡原因也是很簡單,他們共享了太多東西,直接寫到一起多簡單!就像busybox或toolbox集成那麼多命令工具一樣的道理。
/* clear the umask */ umask(0);
清理umask,這個主要是文件權限的問題,設了umask,可以全局mask掉一些文件權限。
/* Get the basic filesystem setup we need put * together in the initramdisk on / and then we'll * let the rc file figure out the rest. */ mkdir(/dev, 0755); mkdir(/proc, 0755); mkdir(/sys, 0755); mount(tmpfs, /dev, tmpfs, MS_NOSUID, 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);
Linux需要的dev,proc, sys等文件系統的加載。
/* indicate that booting is in progress to background fw loaders, etc */ close(open(/dev/.booting, O_WRONLY | O_CREAT, 0000));
創建一個/dev/.booting文件。 /dev是內存文件系統,不會保存的,每次開機都要重新創建。這個是指示,目前在booting過程中,具體干什麼用的,介紹ueventd的時候就清楚了。就是加載設備的fimware用的。
open_devnull_stdio(); klog_init(); property_init();
這三句,分別是將 stdin,stdout和stderr先初始化到/dev/__null__,這樣用printf或其他打印的,都輸出不了,也不會引起其它異常(這個階段,其實不能用,會出錯的)。
注意這個/dev/__null__是臨時起的名字,創建node後刪了,不影響系統真正的/dev/null,這裡只需要fd即可,有了fd後,文件名就無用了,有了還會有干擾。
klog_init,是將init進程中的log,打印到內核printk的log buffer中的方法。這對調試init很有幫助,畢竟此時沒有shell,通用的log輸出方法,如printf等還不能工作,借助底層已有的內核調試功能當然是最好的了。
property的初始化也是在這裡,property讀取可以在各個用戶進程中做,但設置的入口必須是到init進程中來。在4.4上,property這塊修改了好多,現在是通過字典樹的方式存儲,可以支持更多的property屬性。
get_hardware_name(hardware, &revision);
從 /proc/cpuinfo中讀到hardware信息,設置到ro.hardware屬性中,便於後面解析 init.${ro.hardware}.rc使用
process_kernel_cmdline();
kernel的command參數,解析後也放到property中,供以後的子進程或其他服務等使用。
union selinux_callback cb; cb.func_log = klog_write; selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. * This must happen before /dev is populated by ueventd. */ restorecon(/dev); restorecon(/dev/socket); restorecon(/dev/__properties__); restorecon_recursive(/sys);
selinux的初始化和檢查,沒仔細研究selinux,則個檢查過程有時候還挺耗費時間的。
INFO(property init ); if (!is_charger) property_load_boot_defaults();
這個是加載並設置properties。這些是預置的property,通常是/system/build.prop和default.prop文件中預置的那些property。
init_parse_config_file(/init.rc);
這是開始解析init.rc文件了。細節和init.rc的格式、寫法不說了,網上到處都是。主要說一下常見問題:
1. 下載代碼的服務器,umask有時候可能會有影響,init.rc文件other和group用戶是不能有寫權限的,編譯的PC的umask最好設置成0022。
2. init.rc文件嚴格來說只是配置文件,不算腳本,循環、條件判斷等等都不支持的,不要想這裡能干太多事情。
action_for_each_trigger(early-init, action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, wait_for_coldboot_done); queue_builtin_action(mix_hwrng_into_linux_rng_action, mix_hwrng_into_linux_rng); queue_builtin_action(keychord_init_action, keychord_init); queue_builtin_action(console_init_action, console_init); /* execute all the boot actions to get us started */ action_for_each_trigger(init, action_add_queue_tail); ...
這是開始處理init.rc中parser好的各個命令了。
action_for_each_trigger是對此類trigger所在呃所有命令,都加入到actions的list中去。對實際代碼或項目上要全局的看一下,所有的*.rc的同一個trigger都一起處理的。
queue_builtin_action這是內建的actions,也是將actions動作加入到actions list中。
這裡需要注意的是,各個trigger的加載順序,先加入的先執行,後加入的後執行,要特別注意,尤其是要修改init.rc文件的時候,不了解這個容易因為前後依賴關系造成問題。
都准備好了的話,就到了服務處理階段了,這是一個死循環,主要工作就是:
1. 將init.rc及內建的actions命令,一條一條執行
2. 負責對service的管理。
3. 對signal及進程退出的處理
4. 響應property設置的請求(設置都在init中統一設置,讀取進程可以自己讀共享內存)
for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); ...
有時候需要優化開機的話,可以測量一下execute_on_command中的命令執行時間,把較長的(比如大於50ms)的打印出來,再想辦法優化一下。
每次循環,執行一條命令,檢查是否有需要重啟的服務..
nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } }
多路polling,當polling到東西,就執行相應的動作。
timeout時間到,執行下輪循環。init.rc中的command沒處理完的話,timeout是0,這樣之前的actions list可以一順下來都執行掉。
注意,init.rc中定義的服務,是在class_start這個command中做的。
Android Studio常用插件轉載請注明出處: http://blog.csdn.net/crazy1235/article/details/48598803不得不
ShaderShader是一個基類,表示在繪制期間顏色的水平跨度它的子類被嵌入在Paint中使用,調用paint.setShader(shader)。除Bit
最近看了一大堆的自定義View多數都可以充當耗時操作的交互界面,再接再厲再傳一個SubmitView,一個和可用於模仿提交等待與用戶交互用的一個自定義View
如今我們大部分人都在玩微信,都用手機綁定了微信號,手機的功能太強大了,如果手機丟了,或者要換手機號碼怎麼辦?沒關系啦,騰訊公司也會想到這個問題,下面我來為大