本文件將為 Android 裝置。開機時間是系統效能的重要元素 使用者必須等到啟動作業完成後才能使用裝置。裝置 例如較常啟動冷啟動的車輛、 時間至關重要 (沒有人喜歡等候數十秒以輸入 導航目的地)。
Android 8.0 支援多項改善措施,縮短啟動時間 方便您快速進行擴充下表摘要列出這些成效 (評估依據為 Google Pixel 和 Pixel XL 裝置)。
元件 | 提升幅度 |
---|---|
系統啟動載入程式 |
|
裝置核心 |
|
I/O 調整 |
|
init.*.rc |
|
開機動畫 |
|
SELinux 政策 | genfscon 節省了 0.2 秒 |
最佳化系統啟動載入程式
如要改善系統啟動載入程式以縮短啟動時間,請按照下列步驟操作:
- 記錄功能:
- 停用將記錄寫入 UART 的功能,因為可能需要大量時間 。(我們發現 Google Pixel 裝置的系統啟動載入程式 1.5s 變慢)。
- 僅記錄錯誤情況,並考慮將其他資訊儲存至記憶體 並使用不同的擷取機制
- 針對核心解壓縮,請考慮將 LZ4 用於現代硬體 (範例:patch),而非 GZIP。注意事項 不同的核心壓縮選項可以有不同的載入方式 某些選項的效果可能會比其他選項來得好 特定硬體
- 檢查去彈跳/特殊模式進入不必要的等待時間,並盡可能減少 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件
- 將系統啟動載入程式的啟動時間,以 cmdline 的形式傳遞到核心。
- 檢查 CPU 時脈並考慮採用平行處理功能 (需要多核心支援) 用於核心載入和初始化 I/O。
最佳化 I/O 效率
如要加快啟動時間及讀取資料,關鍵在於改善 I/O 效率 所有非必要的資產都應延後到啟動 (在 Google Pixel 上 啟動時讀取約 1.2GB 的資料)。
調整檔案系統
開始讀取檔案或檔案開始讀取時,Linux kernel 讀取作業會開始執行 系統會依序讀取區塊,因此必須調整 I/O 排程器 參數,專供啟動程序使用 而非一般應用程式) 的特性
支援流暢 (A/B) 更新功能的裝置可從檔案系統中大獲益 在首次啟動時調整 (例如 Google Pixel 上的 20 秒)舉例來說 下列 Google Pixel 參數:
on late-fs # boot time fs tune # boot time fs tune write /sys/block/sda/queue/iostats 0 write /sys/block/sda/queue/scheduler cfq write /sys/block/sda/queue/iosched/slice_idle 0 write /sys/block/sda/queue/read_ahead_kb 2048 write /sys/block/sda/queue/nr_requests 256 write /sys/block/dm-0/queue/read_ahead_kb 2048 write /sys/block/dm-1/queue/read_ahead_kb 2048 on property:sys.boot_completed=1 # end boot time fs tune write /sys/block/sda/queue/read_ahead_kb 512 ...
其他
- 使用核心設定開啟 dm-verity 雜湊預先擷取大小 DM_VERITY_HASH_PREFETCH_MIN_SIZE (預設大小為 128)。
- 為提高檔案系統的穩定性,並降低以下裝置的強制檢查機制: 每次啟動時,您都必須將 TARGET_USES_MKE2FS BoardConfig.mk。
分析 I/O
如要瞭解啟動期間的 I/O 活動,請使用核心 ftrace 資料 ( systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
如要細分每個檔案的檔案存取權,請對核心進行下列變更 (僅限開發核心;不適用於實際工作環境核心):
diff --git a/fs/open.c b/fs/open.c index 1651f35..a808093 100644 --- a/fs/open.c +++ b/fs/open.c @@ -981,6 +981,25 @@ } EXPORT_SYMBOL(file_open_root); +static void _trace_do_sys_open(struct file *filp, int flags, int mode, long fd) +{ + char *buf; + char *fname; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + fname = d_path(&filp-<f_path, buf, PAGE_SIZE); + + if (IS_ERR(fname)) + goto out; + + trace_printk("%s: open(\"%s\", %d, %d) fd = %ld, inode = %ld\n", + current-<comm, fname, flags, mode, fd, filp-<f_inode-<i_ino); +out: + kfree(buf); +} + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; @@ -1003,6 +1022,7 @@ } else { fsnotify_open(f); fd_install(fd, f); + _trace_do_sys_open(f, flags, mode, fd);
使用下列指令碼有助於分析啟動效能。
system/extras/boottime_tools/bootanalyze/bootanalyze.py
依據啟動程序中的重要步驟細分測量啟動時間。system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace
提供每個檔案的存取權資訊。system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace
提供系統層級的詳細資料。
最佳化 init.*.rc
Init 是從核心之間 到建立架構為止的橋樑 裝置在不同初始階段花費幾秒鐘的時間。
平行執行工作
雖然目前的 Android init 採用了較新的執行緒程序 仍可同時執行某些工作
- 在 Shell 指令碼服務中執行慢指令,之後再依據
等待特定資源Android 8.0 支援這個使用情境,並搭配
wait_for_property
指令。 - 找出 init 中的緩慢作業。系統會記錄 init 指令
exec/wait_for_prop 或任何花費較長時間的動作 (在 Android 8.0 中,任何指令
需要超過 50 毫秒的時間)。例如:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
查看這份記錄也許能找出改善方向。
- 盡早啟動服務並啟用周邊裝置。適用對象 例如,有些 SOC 要求先啟動安全相關服務,才能啟動 SurfaceFlinger。在 ServiceManager 傳回「wait for」動作時,查看系統記錄。 「服務」:通常代表必須啟動相依服務 首先。
- 移除 init.*.rc 中所有未使用的服務和指令。未使用 早期階段 init 應延遲以完成啟動。
注意:屬性服務屬於 init 程序的一部分,因此,請呼叫
如果 init 處於忙碌狀態,setproperty
可能會長時間延遲
內建的指令
使用排程器調整功能
使用排程器調整功能進行早期啟動。Google Pixel 示例:
on init # boottime stune write /dev/stune/schedtune.prefer_idle 1 write /dev/stune/schedtune.boost 100 on property:sys.boot_completed=1 # reset stune write /dev/stune/schedtune.prefer_idle 0 write /dev/stune/schedtune.boost 0 # or just disable EAS during boot on init write /sys/kernel/debug/sched_features NO_ENERGY_AWARE on property:sys.boot_completed=1 write /sys/kernel/debug/sched_features ENERGY_AWARE
某些服務可能需要在啟動期間提高優先順序。範例如下:
init.zygote64.rc: service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root ...
盡早啟動 zygote
採用檔案型加密功能的裝置可在 zygote-start 時自動啟動 zygote 觸發 (預設情況下,zygote 會在類別主要系統啟動, zygote-start)。如要執行這項操作,請務必允許 zygote 在所有 CPU 中執行 (即 錯誤的 cpuset 設定可能會強制在特定 CPU 執行 zygote)。
停用省電功能
在裝置啟動時,UFS 和/或 CPU 等元件的省電設定 您可以停用主機端。
注意:省電模式的啟用時間應為 以提升效率
on init # Disable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 0 write /sys/module/lpm_levels/parameters/sleep_disabled Y on property:sys.boot_completed=1 # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/module/lpm_levels/parameters/sleep_disabled N on charger # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/class/typec/port0/port_type sink write /sys/module/lpm_levels/parameters/sleep_disabled N
延後非關鍵初始化
一般初始化作業 (例如 ZRAM) 可推遲至 boot_complete
。
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
最佳化啟動動畫
請按照下列提示改善啟動動畫的成效。
設定提早開始
Android 8.0 可在掛接使用者資料之前,提早啟動啟動動畫 不過,即使在 Android 8.0 中使用新的 ext4 工具鍊 但因安全原因而會定期觸發,導致 啟動啟動服務
若要提早啟動啟動程序,請將 fstab 掛接分為兩個階段:
- 在早期階段,只掛接分區 (例如
system/
和vendor/
),不需要執行 然後啟動啟動動畫服務及其依附元件 (例如 servicemanager 和 Surfaceflinger)。 - 在第二階段中,掛接該分區 (例如
data/
) 的分區 執行檢查
無論如何 指怪
完成清潔
收到離開信號後,啟動動作會播放最後一個部分, 導致啟動時間變慢無須長時間即可開機的系統 可以有效隱藏任何改善效果的動畫建議做法 同時盡量減少重複循環和結尾
最佳化 SELinux
請使用下列提示將 SELinux 最佳化,以縮短啟動時間。
- 使用簡潔的規則運算式 (regex)。規則運算式的格式有誤
比對 SELinux 政策
sys/devices
於file_contexts
。例如/sys/devices/.*abc.*(/.*)?
不小心強制掃描全部 有/sys/devices
個包含「abc」的子目錄,可供比對/sys/devices/abc
和/sys/devices/xyz/abc
兩者皆是如此。 如果將這個規則運算式提升為/sys/devices/[^/]*abc[^/]*(/.*)?
, 只啟用/sys/devices/abc
的比對。 - 將標籤移至 genfscon。 這個現有的 SELinux 功能會將檔案比對前置字串傳遞至核心: SELinux 二進位檔,其中核心會將其套用至由核心產生的 檔案系統這也有助於修正標記核心建立錯誤的檔案 嘗試存取的使用者空間程序之間發生的競爭狀況 才會重新加上標籤。
工具和方法
請使用下列工具收集要最佳化目標的資料。
開機圖
Bootchart 會針對整個程序提供全部的 CPU 和 I/O 負載細目 有些人會將 Cloud Storage 視為檔案系統 但實際上不是無須重新建構系統映像檔,只要快速套用即可 執行例行性檢查
如何啟用 bootchart:
adb shell 'touch /data/bootchart/enabled'
adb reboot
啟動後,擷取開機圖表:
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
完成後,請刪除 /data/bootchart/enabled
,以免收集到
因此資料的規模
bootchart.png
不存在的錯誤訊息,請執行
包括:
- 執行下列指令:
sudo apt install python-is-python3
cd ~/Documents
git clone https://github.com/xrmx/bootchart.git
cd bootchart/pybootchartgui
mv main.py.in main.py
- 更新「
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
」 指向pybootchartgui
的本機副本 (位於~/Documents/bootchart/pybootchartgui.py
)
Systrace
Systrace 允許在啟動期間收集核心和 Android 追蹤記錄。 以視覺化方式呈現 Systrace 有助於分析 開機。(不過,如要檢視 反而會更容易直接查看核心追蹤記錄)。
如何在開機時啟用 systrace:
- 在
frameworks/native/cmds/atrace/atrace.rc
中,將:write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0
到:
# write /sys/kernel/debug/tracing/tracing_on 0 # write /sys/kernel/tracing/tracing_on 0
- 在
device.mk
檔案中,新增以下這行內容:PRODUCT_PROPERTY_OVERRIDES += debug.atrace.tags.enableflags=802922 PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=0
- 在裝置的
BoardConfig.mk
檔案中新增以下內容:BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
- 在裝置專屬的
init.rc
檔案中,新增以下內容:on property:sys.boot_completed=1 // This stops tracing on boot complete write /d/tracing/tracing_on 0 write /d/tracing/events/ext4/enable 0 write /d/tracing/events/f2fs/enable 0 write /d/tracing/events/block/enable 0
-
啟動後,擷取追蹤記錄:
adb root && adb shell atrace --async_stop -z -c -o /data/local/tmp/boot_trace
adb pull /data/local/tmp/boot_trace
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace
這項操作會啟用追蹤 (預設為停用)。
如需詳細的 I/O 分析,請同時加入區塊和 ext4 與 f2fs。