يوفّر هذا المستند إرشادات للشركاء لتحسين أوقات بدء التشغيل لأجهزة Android معيّنة. وقت بدء التشغيل هو مكوّن مهم من أداء النظام، لأنّه على المستخدمين الانتظار حتى اكتمال عملية بدء التشغيل قبل أن يتمكّنوا من استخدام الجهاز. بالنسبة إلى الأجهزة، مثل السيارات التي يتم فيها تشغيل الجهاز من جديد بشكل متكرّر، من المهم أن يكون وقت التشغيل سريعًا (لا أحد يحب الانتظار لعدّة عشرات من الثواني لمجرد إدخال وجهةnavigate).
يتيح نظام التشغيل Android 8.0 تقليل أوقات بدء التشغيل من خلال توفير العديد من التحسينات على مستوى مجموعة من المكوّنات. يلخّص الجدول التالي تحسينات الأداء هذه (كما تم قياسها على أجهزة Google Pixel وPixel XL).
المكوّن | تحسين |
---|---|
برنامج الإقلاع |
|
نواة الجهاز |
|
ضبط وحدات الإدخال والإخراج |
|
init.*.rc |
|
الصورة المتحركة لبدء التشغيل |
|
سياسة SELinux | تم توفير 0.2 ثانية من خلال genfscon |
تحسين برنامج الإقلاع
لتحسين أداة تحميل البرامج لتحسين أوقات التشغيل:
- بالنسبة إلى التسجيل:
- أوقِف كتابة السجلّ إلى UART لأنّ ذلك قد يستغرق وقتًا طويلاً مع الكثير من عمليات التسجيل. (على أجهزة Google Pixel، تبيّن لنا أنّه يبطئ برنامج الإقلاع لمدة 1.5 ثانية).
- سجِّل حالات الخطأ فقط، وفكِّر في تخزين المعلومات الأخرى في الذاكرة باستخدام آلية منفصلة لاستردادها.
- فك ضغط النواة: ننصحك باستخدام LZ4 للأجهزة الحديثة بدلاً من GZIP (مثال على تصحيح). يُرجى العِلم أنّه قد تختلف أوقات التحميل و فك الضغط للخيارات المختلفة لضغط النواة، وقد تعمل بعض الخيارات بشكل أفضل من غيرها مع الأجهزة المحدّدة.
- تحقّق من أوقات الانتظار غير الضرورية لإزالة الارتعاش/دخول الوضع الخاص وتجنَّب تلك الأوقات قدر الإمكان.
- تمرير وقت التمهيد الذي تم قضاؤه في أداة التمهيد إلى النواة كسلسلة أوامر
- تحقَّق من ساعة وحدة المعالجة المركزية (CPU) وفكِّر في استخدام تقنية التوازي (يتطلب ذلك دعم المعالجات متعددة النوى) لتحميل النواة وبدء عمليات الإدخال/الإخراج.
تحسين كفاءة الإدخال/الإخراج
إنّ تحسين كفاءة الإدخال/الإخراج أمر مهم لتسريع وقت التشغيل، ويجب تأجيل قراءة أي شيء غير ضروري إلى ما بعد التشغيل (على هاتف Google Pixel، تتم قراءة حوالي 1.2 غيغابايت من البيانات عند التشغيل).
ضبط نظام الملفات
يتم تفعيل ميزة "القراءة المسبقة" لنظام التشغيل Linux عند قراءة ملف من البداية أو عند قراءة وحدات متسلسلة، ما يجعل من الضروري ضبط مَعلمات جدولة الإدخال/الإخراج لبدء التشغيل على وجه التحديد (الذي يختلف عن التطبيقات العادية من حيث تحديد الحمولة).
تستفيد الأجهزة التي تتيح التحديثات السلسة (A/B) بشكل كبير من ملف النظام الترميم عند التمهيد لأول مرة (مثلاً 20 ثانية على Google Pixel). على سبيل المثال، عدّلنا المَعلمات التالية لجهاز 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 باستخدام إعدادات kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (الحجم التلقائي هو 128).
- لتحسين ثبات نظام الملفات وإيقاف عملية التحقّق الإجباري التي تحدث عند كل عملية تشغيل، استخدِم أداة إنشاء ext4 الجديدة من خلال ضبط TARGET_USES_MKE2FS فيملف BoardConfig.mk.
تحليل 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 هو الجسر الذي يربط بين النواة وإطار العمل، ويقضي الأجهزة عادةً بضع ثوانٍ في مراحل مختلفة من عملية Init.
تنفيذ المهام بالتوازي
على الرغم من أنّ عملية Android init الحالية هي عملية أحادية السلسلة تقريبًا، فإنه لا يزال بإمكانك تنفيذ بعض المهام بالتوازي.
- تنفيذ الأوامر البطيئة في خدمة نص برمجي لنظام التشغيل وبدء تنفيذها لاحقًا من خلال
الانتظار لخاصية معيّنة يتيح نظام التشغيل 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
قد تشير مراجعة هذا السجلّ إلى فرص لإجراء تحسينات.
- ابدأ الخدمات وفعِّل الأجهزة الطرفية في المسار الحرج مبكرًا. على سبيل المثال، تتطلّب بعض وحدات التحكّم في العمليات الأمنية بدء الخدمات ذات الصلة بالأمان قبل بدء SurfaceFlinger. راجِع سجلّ النظام عندما يعرض ServiceManager رسالة "الانتظار لبدء الخدمة"، فهذا عادةً ما يشير إلى أنّه يجب أولاً بدء الخدمة التابعة.
- أزِل أي خدمات وأوامر غير مستخدَمة في init.*.rc. يجب تأجيل أيّ عملية غير مستخدَمة في مرحلة الإعداد المبكرة إلى أن تكتمل عملية التشغيل.
ملاحظة: خدمة Property هي جزء من عملية init، لذا قد يؤدي استدعاء
setproperty
أثناء عملية التمهيد إلى تأخير طويل إذا كانت init مشغولة في
الطلبات المضمّنة.
استخدام ميزة "ضبط المخطِّط"
استخدِم ميزة "ضبط المخطِّط" لبدء التشغيل المبكر. مثال من هاتف 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 تلقائيًا في فئة main، وهي لاحقة بكثير من zygote-start). عند إجراء ذلك، احرص على السماح بتشغيل zygote في جميع وحدات المعالجة المركزية (لأنّه قد يؤدي ضبط cpuset بشكلٍ خاطئ إلى فرض تشغيل 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 بدء عرض صورة التمهيد مبكرًا، قبل تركيب قسم userdata. ومع ذلك، حتى عند استخدام سلسلة أدوات ext4 الجديدة في Android 8.0، يستمر تشغيل fsck بشكل دوري لأسباب تتعلق بالسلامة، ما يؤدي إلى تأخير بدء خدمة bootanimation.
لبدء عرض bootanimation مبكرًا، يمكنك تقسيم عملية تحميل fstab إلى مرحلتين:
- في المرحلة المبكرة، يجب تركيب الأقسام فقط (مثل
system/
وvendor/
) التي لا تتطلّب بدء عمليات التحقّق، ثم بدء خدمات الرسوم المتحرّكة لبدء التشغيل والمكوّنات التابعة لها (مثل servicemanager وsurfaceflinger). - في المرحلة الثانية، يمكنك تركيب الأقسام (مثل
data/
) التي تتطلّب إجراء عمليات التحقّق.
سيتم تشغيل صورة التمهيد المتحركة بشكل أسرع بكثير (وبوقت ثابت) بغض النظر عن fsck.
إنهاء التنظيف
بعد تلقّي إشارة الخروج، يشغِّل bootanimation الجزء الأخير، ويمكن أن يؤدي طوله إلى إبطاء وقت التشغيل. لا يحتاج النظام الذي يتم تشغيله بسرعة إلى استخدام مؤثرات متحركة مديدة يمكن أن تحجب بشكل فعال أي تحسينات تم إجراؤها. ننصحك بجعل كلّ من القسم المتكرّر والقسم الأخير قصيرَين.
تحسين 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
يوفّر Bootchart تفاصيل عن حمولة وحدة المعالجة المركزية وعمليات الإدخال/الإخراج لجميع العمليات في النظام بأكمله. ولا يتطلّب هذا الإجراء إعادة إنشاء صورة النظام، ويمكن استخدامه كفحص سريع للتحقّق من صحة النظام قبل الانتقال إلى systrace.
لتفعيل 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 جمع عمليات تتبُّع كل من kernel و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
يؤدي ذلك إلى تفعيل التتبُّع (الذي يكون غير مفعَّل تلقائيًا).
للحصول على تحليل مفصّل لعمليات الإدخال/الإخراج، أضِف أيضًا block وext4 وf2fs.