เราเคยครอบคลุมคู่มือเกี่ยวกับ Android kernels เช่น “How to Build a Custom Kernel” และ “Best Custom Kernels for Android” แต่วันนี้เราจะแสดงวิธี upstream kernel ของคุณกับ Linux stable ล่าสุด
โปรดทราบว่า đâyคือสิ่งที่ ขั้นสูง (advanced) – หากคุณไม่เคยคอมไพล์ kernel มาก่อน คุณควรทำตามคู่มือ “How to Build a Custom Kernel” ที่ลิงก์ด้านบน และคู่มือนี้จะเกี่ยวข้องกับ cherry-picking และการ merge commits จาก Linux-stable kernel ล่าสุดกับ Android kernel ของคุณก่อนคอมไพล์
การ upstream Android kernel ของคุณไปยัง Linux stable ล่าสุดมีประโยชน์มาก เช่น ทันสมัยกับคอมมิตความปลอดภัยและการแก้ไขบั๊กล่าสุด – เราจะอธิบายข้อดีและข้อเสียบางส่วนในภายหลังของคู่มือนี้
Linux-Stable kernel คืออะไร?

Linux-stable ตามที่ชื่อบ่งชี้ คือแขนที่เสถียรของ Linux kernel อีกแขนหนึ่งเรียกว่า “mainline” ซึ่งเป็น master branch การพัฒนา Linux kernel ทั้งหมดเกิดขึ้นใน mainline และโดยทั่วไปจะทำตามกระบวนการนี้:
- Linus Torvalds จะรับชุดแพตช์จากผู้ดูแลของเขาเป็นเวลาสองสัปดาห์
- หลังจากสองสัปดาห์นี้ เขาจะปล่อย kernel rc1 (เช่น 4.14-rc1)
- สำหรับแต่ละสัปดาห์ใน 6-8 สัปดาห์ถัดไป เขาจะปล่อย RC อีกตัว (เช่น 4.14-rc2, 4.14-rc3, เป็นต้น) ซึ่งมีเฉพาะการแก้ไขบั๊กและการถอยหลัง (regression fixes)
- เมื่อถือว่าเสถียรแล้ว จะถูกปล่อยเป็น tarball สำหรับดาวน์โหลดบน kernel.org (เช่น 4.14)
LTS kernels คืออะไร?
ทุกปี Greg จะเลือก kernel หนึ่งและดูแลรักษาเป็นเวลาสองปี (LTS) หรือหกปี (extended LTS) พวกเขาออกแบบมาเพื่อผลิตภัณฑ์ที่ต้องการความเสถียร (เช่น โทรศัพท์ Android หรืออุปกรณ์ IoT อื่นๆ) กระบวนการเหมือนกันทุกประการ เพียงแค่เกิดขึ้นนานกว่า ปัจจุบันมี LTS kernels หกตัว (ซึ่งมองได้ตลอดที่ kernel.org releases page):
- 4.14 (LTS), ดูแลโดย Greg Kroah-Hartman
- 4.9 (LTS), ดูแลโดย Greg Kroah-Hartman
- 4.4 (eLTS), ดูแลโดย Greg Kroah-Hartman
- 4.1(LTS), ดูแลโดย Sasha Levin
- 3.16 (LTS), ดูแลโดย Ben Hutchings
- 3.2 (LTS), ดูแลโดย Ben Hutchings
ประโยชน์ของการ upstream Android kernel ไป Linux Stable คืออะไร?
เมื่อช่องโหว่สำคัญถูกเปิดเผย/แก้ไข Stable kernels จะเป็นตัวแรกที่ได้รับมัน ดังนั้น Android kernel ของคุณจะปลอดภัยกว่าต่อการโจมตี ช่องโหว่ด้านความปลอดภัย และบั๊กทั่วไป
Linux stable รวมการแก้ไขสำหรับไดรเวอร์มากมายที่อุปกรณ์ Android ของฉันไม่ได้ใช้ ไม่ใช่ส่วนใหญ่ที่ไม่จำเป็น?
ใช่และไม่ใช่ ขึ้นอยู่กับวิธีที่คุณนิยาม “ส่วนใหญ่” Linux kernel อาจรวมโค้ดมากที่ไม่ได้ใช้ในระบบ Android แต่ไม่ได้รับประกันว่าจะไม่มีความขัดแย้งจากไฟล์เหล่านั้นเมื่อ merge เวอร์ชันใหม่! เข้าใจได้ว่าเกือบ ไม่มีใคร สร้างทุกส่วนของ kernel แม้แต่วิกลินุกซ์ยอดนิยมอย่าง Ubuntu หรือ Mint นี่ไม่ได้หมายความว่าคุณไม่ควรนำการแก้ไขเหล่านี้มา เพราะมีการแก้ไขสำหรับไดรเวอร์ที่คุณ ใช้ อยู่ จัดตัวอย่าง arm/arm64 และ ext4 ซึ่งเป็นสถาปัตยกรรมและระบบไฟล์ที่พบบ่อยที่สุดสำหรับ Android ตามลำดับ ใน 4.4 จาก 4.4.78 (เวอร์ชันของ Oreo CAF tag ล่าสุด) ถึง 4.4.121 (latest upstream tag) นี่คือตัวเลขคอมมิตสำหรับระบบเหล่านั้น:
nathan@flashbox ~/kernels/linux-stable (master) $ git log --format=%h v4.4.78..v4.4.121 | wc -l 2285 nathan@flashbox ~/kernels/linux-stable (master) $ git log --format=%h v4.4.78..v4.4.121 arch/arm | wc -l 58 nathan@flashbox ~/kernels/linux-stable (master) $ git log --format=%h v4.4.78..v4.4.121 arch/arm64 | wc -l 22 nathan@flashbox ~/kernels/linux-stable (master) $ git log --format=%h v4.4.78..v4.4.121 fs/ext4 | wc -l 18
ส่วนที่ใช้เวลานานที่สุดคือการนำขึ้นมาครั้งแรก (initial bring up) เมื่อคุณทันสมัยแล้ว ใช้เวลาไม่กี่อย่างในการ merge รีลีสใหม่ ซึ่งโดยปกติมีไม่เกิน 100 คอมมิต ประโยชน์ที่นี่นำมา (ความเสถียรมากขึ้นและความปลอดภัยดีขึ้นสำหรับผู้ใช้) ควรทำให้กระบวนการนี้จำเป็น
วิธี Merge Linux Stable Kernel เข้า Android Kernel
ขั้นแรก คุณต้อง 알아 버 kernel version ที่อุปกรณ์ Android ของคุณกำลังรัน
แม้จะดูเหมือนเรื่องจิ๋ว แต่จำเป็นต้องรู้ว่าคุณต้องเริ่มที่ไหน รันคำสั่งต่อไปนี้ใน kernel tree ของคุณ:
make kernelversion
มันจะคืนค่าเวอร์ชันที่คุณอยู่ ตัวเลขสองตัวแรกจะใช้เพื่อหา branch ที่คุณต้องการ (เช่น linux-4.4.y สำหรับ kernel 4.4 ใดๆ) และตัวเลขสุดท้ายจะใช้กำหนดเวอร์ชันที่คุณต้องเริ่ม merge (เช่น หากคุณอยู่ที่ 4.4.21 คุณจะ merge 4.4.22 ต่อไป)
ดึง kernel source ล่าสุดจาก kernel.org
kernel.org เป็นที่เก็บ kernel source ล่าสุดใน linux-stable repository ที่ด้านล่างของหน้านั้น จะมีลิงก์ fetch สามลิงก์ ในประสบการณ์ของผม mirror ของ Google มักเร็วที่สุด แต่ผลลัพธ์อาจแตกต่าง รันคำสั่งต่อไปนี้:
git remote add linux-stable https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git git fetch linux-stable
ตัดสินใจว่าจะ merge kernel ทั้งหมดหรือ cherry-pick commits
ต่อไป คุณจะต้องเลือกว่าจะ merge commits หรือ cherry-pick นี่คือข้อดีและข้อเสียของแต่ละอย่าง และเมื่อไหร่คุณอาจต้องการทำแต่ละอย่าง
หมายเหตุ: หาก kernel source ของคุณอยู่ในรูปแบบ tarball คุณน่าจะต้อง cherry-pick มิฉะนั้นคุณจะได้ข้อขัดแย้งไฟล์หลายพันรายการ เพราะ git กำลังสร้างประวัติขึ้นจาก upstream อย่างเดียว ไม่ใช่สิ่งที่ OEM หรือ CAF เปลี่ยนไป เพียงข้ามไปที่ขั้นตอนที่ 4
Cherry-picking:
ข้อดี:
- ง่ายกว่าในการแก้ไขข้อขัดแย้ง เพราะคุณรู้ว่าคอมมิตไหนก่อให้เกิดปัญหา
- ง่ายกว่าในการ rebase เพราะแต่ละคอมมิตอยู่บนตัวของมันเอง
- ง่ายกว่าในการ bisect หากพบปัญหา
ข้อเสีย:
- ใช้เวลานานกว่า เพราะแต่ละคอมมิตต้อง pick แยกกัน
- ยากกว่าเล็กน้อยในการบอกว่าคอมมิตมาจาก upstream ใน מבט ראשון
Merge
ข้อดี:
- เร็วกว่า เพราะคุณไม่ต้องรอให้แพตช์ที่สะอาดทั้งหมด merge
- ง่ายกว่าในการเห็นเมื่อคอมมิตมาจาก upstream เพราะคุณจะไม่ใช่ committer ผู้ดูแล upstream จะเป็นคนนั้น
ข้อเสีย:
- การแก้ไขข้อขัดแย้งอาจยากกว่าเล็กน้อย เพราะคุณต้องค้นหาว่าคอมมิตไหนก่อให้เกิดข้อขัดแย้งโดยใช้ git log/git blame มันจะไม่บอกคุณโดยตรง
- Rebasing ยาก เพราะคุณไม่สามารถ rebase merge ได้ มันจะเสนอให้ cherry-pick คอมมิตทั้งหมดแยกกัน อย่างไรก็ตาม คุณไม่ควร rebase บ่อย แทนที่จะใช้ git revert และ git merge ที่เป็นไปได้
ผมแนะนำให้ทำ cherry-pick เพื่อหาข้อขัดแย้งปัญหาเริ่มต้น ทำ merge แล้ว revert คอมมิตที่มีปัญหา後來 เพื่อให้การอัปเดตง่ายขึ้น (เพราะ merge เร็วกว่าหลังจากทันสมัยแล้ว)
เพิ่มคอมมิตเข้า source ของคุณ ทีละเวอร์ชัน
ส่วนสำคัญที่สุดของกระบวนการนี้คือส่วน “ทีละเวอร์ชัน” อาจมีแพตช์ที่มีปัญหาใน upstream series ของคุณ ซึ่งอาจก่อให้เกิดปัญหากับการบูท หรือทำลายสิ่งต่างๆ เช่น เสียงหรือการชาร์จ (อธิบายในส่วนเคล็ดลับและเทคนิค) การเปลี่ยนแปลงเวอร์ชันแบบเพิ่มขึ้น (incremental) สำคัญเพราะเหตุนี้ ง่ายกว่าที่จะหาปัญหาใน 50 คอมมิต แทนที่จะเป็น 2000+ คอมมิตสำหรับบางเวอร์ชัน ผมแนะนำให้ทำ full merge เพียงครั้งเดียวเมื่อคุณรู้คอมมิตที่มีปัญหาและการแก้ไขข้อขัดแย้งทั้งหมดแล้ว
Cherry-picking
รูปแบบ:
git cherry-pick <previous_tag>..<next_tag>
ตัวอย่าง:
git cherry-pick v3.10.73..v3.10.74
Merge
รูปแบบ:
git merge <next_tag>
ตัวอย่าง:
git merge v3.10.74
แนะนำให้ติดตามข้อขัดแย้งใน merge commits โดยการลบเครื่องหมาย #
วิธีแก้ไขข้อขัดแย้ง (How to Resolve Conflicts)
เราไม่สามารถให้คู่มือขั้นตอนต่อขั้นตอนสำหรับการแก้ไขข้อขัดแย้งทุกอย่างได้ เนื่องจากเกี่ยวข้องกับความรู้ที่ดีในภาษา C แต่นี่คือเคล็ดลับบางอย่าง
หากคุณกำลัง merge ให้หาว่าคอมมิตไหนก่อให้เกิดข้อขัดแย้ง คุณสามารถทำได้สองวิธี:
- git log -p v$(make kernelversion)..<latest_tag> เพื่อรับการเปลี่ยนแปลงระหว่างเวอร์ชันปัจจุบันของคุณและล่าสุดจาก upstream ธง -p จะให้การเปลี่ยนแปลงที่แต่ละคอมมิตทำ ดังนั้นคุณสามารถมองเห็นได้
- รัน git blame บนไฟล์เพื่อรับแฮชของแต่ละคอมมิตในพื้นที่นั้น คุณสามารถรัน git show –format=fuller เพื่อดูว่า committer มาจาก mainline/stable, Google, หรือ CodeAurora
- หาว่าคุณมีคอมมิตนั้นอยู่แล้วหรือไม่ บางผู้จัดจำหน่ายเช่น Google หรือ CAF จะพยายามดู upstream สำหรับบั๊กสำคัญ เช่น การแก้ไข Dirty COW และ backports ของพวกเขาอาจขัดแย้งกับ upstream คุณสามารถรัน git log –grep=”<part_of_commit_message>” และดูว่ามันคืนค่าอะไร หากคืนค่า คุณสามารถข้ามคอมมิต (หาก cherry-picking ใช้ git reset –hard && git cherry-pick –continue) หรือละเว้นข้อขัดแย้ง (ลบ <<<<<< และทุกอย่างระหว่าง ====== และ >>>>>>)
- หาว่ามี backport ที่ทำให้การแก้ไขขัดขวางหรือไม่ Google และ CAF ชอบ backport แพตช์บางอย่างที่ stable ไม่ได้ Stable จะต้องปรับการแก้ไขของคอมมิต mainline ให้เข้ากับการขาดแพตช์บางอย่างที่ Google เลือก backport คุณสามารถดูคอมมิต mainline ได้โดยรัน git show <hash> (mainline hash จะพร้อมใช้งานในข้อความคอมมิตของ stable commit) หากมี backport ทำให้ขัดขวาง คุณสามารถทิ้งการเปลี่ยนแปลง หรือใช้เวอร์ชัน mainline (ซึ่งเป็นสิ่งที่คุณมักจะต้องทำ)
- อ่านว่าคอมมิตกำลังพยายามทำอะไร และดูว่าปัญหาได้รับการแก้ไขแล้วหรือยัง บางครั้ง CAF อาจแก้ไขบั๊กโดยอิสระจาก upstream หมายความว่าคุณสามารถเขียนทับการแก้ไขของพวกเขาด้วย upstream หรือทิ้งมัน เช่นเดียวกับข้างต้น
มิฉะนั้น อาจเป็นผลของการเพิ่มของ CAF/Google/OEM ซึ่งในกรณีนี้คุณเพียงต้องเลื่อนลำดับบางอย่าง
นี่คือ mirror ของ linux-stable kernel.org repository บน GitHub ซึ่งอาจง่ายกว่าสำหรับการค้นหารายการคอมมิตและ diffs สำหรับการแก้ไขข้อขัดแย้ง แนะนำให้ไปที่ commit list view ก่อนและค้นหาคอมมิตที่มีปัญหาเพื่อดู original diff เปรียบเทียบกับของคุณ
ตัวอย่าง URL: https://github.com/nathanchance/linux-stable/commits/linux-3.10.y/arch/arm64/mm/mmu.c
คุณยังสามารถทำได้ผ่านบรรทัดคำสั่ง:
git log <current_version>..<version_being_added> <path> git show <hash>
การแก้ไขข้อขัดแย้งทั้งหมดเกี่ยวข้องกับบริบท (context) สิ่งที่คุณควรทำ เสมอ คือ ตรวจสอบให้แน่ใจว่า final diff ของคุณตรงกับ upstream โดยการรันคำสั่งต่อไปนี้ในสองหน้าต่างแยกกัน:
git diff HEAD git diff v$(make kernelversion)..$(git tag --sort=-taggerdate -l v$(make kernelversion | cut -d . -f 1,2)* | head -n1)
เปิดใช้งาน rerere
Git มีฟีเจอร์ชื่อ rerere (ย่อมาจาก Reuse Recorded Resolution) หมายความว่าเมื่อมันตรวจพบข้อขัดแย้ง มันจะบันทึกวิธีที่คุณแก้ไข เพื่อให้คุณนำไปใช้ซ้ำได้ภายหลัง นี่เป็นประโยชน์มากสำหรับทั้ง chronic rebasers ด้วย merge และ cherry-picking เพราะคุณจะแค่ต้องรัน git add . && git <merge|cherry-pick> –continue เมื่อทำ upstream bringup ซ้ำ ข้อขัดแย้งจะถูกแก้ไขเหมือนที่คุณแก้ไขก่อนหน้านี้
สามารถเปิดใช้งานได้โดยการรันคำสั่งต่อไปนี้ใน kernel repo ของคุณ:
git config rerere.enabled true
วิธี git bisect เมื่อเจอ compiler หรือ runtime error
เนื่องจากคุณจะเพิ่มคอมมิตจำนวนมาก มันเป็นไปได้มากที่คุณจะนำ compiler หรือ runtime error เข้ามา แทนที่จะยอมแพ้ คุณสามารถใช้เครื่องมือ bisect ในตัวของ git เพื่อหาสาเหตุรากฐานของปัญหา! ในในอุดมคติ คุณจะ build และ flash kernel ทุกเวอร์ชันเมื่อคุณเพิ่มมัน ดังนั้น bisecting จะใช้เวลาน้อยกว่าหากต้องการ แต่คุณสามารถ bisect 5000 คอมมิตได้โดยไม่มีปัญหา
git bisect จะทำอะไรคือ รับช่วงคอมมิต จากที่ปัญหามีอยู่ ไปที่ที่ไม่มีปัญหา และเริ่มแบ่งช่วงคอมมิตครึ่งๆ ทำให้คุณ build และ test และบอกมันว่าดีหรือไม่ มันจะทำซ้ำจนกว่าจะ吐ออกคอมมิตที่ก่อให้เกิดปัญหาของคุณ ที่จุดนั้น คุณสามารถแก้ไขมันหรือ revert มัน
- เริ่ม bisecting: git bisect start
- ติดป้าย current revision ว่า bad: git bisect bad
- ติดป้าย revision หนึ่งว่า good: git bisect good <sha1>
- Build ด้วย revision ใหม่
- ขึ้นอยู่กับผลลัพธ์ (หากปัญหามีอยู่หรือไม่) บอก git: git bisect good OR git bisect bad
- ทำซ้ำขั้นตอน 4-5 จนกว่าจะพบคอมมิตที่ก่อให้เกิดปัญหา!
- Revert หรือแก้ไขคอมมิตที่มีปัญหา
หมายเหตุ: Merger จะต้องรัน git rebase -i <good_sha> ชั่วคราวเพื่อ apply ทุกแพตช์ไปที่ branch ของคุณสำหรับ bisecting ที่เหมาะสม เนื่องจาก bisecting ด้วย merges ที่อยู่ที่นั่นจะบ่อยครั้ง checkout ไปที่ upstream commits หมายความว่าคุณไม่มีคอมมิตเฉพาะ Android ผมสามารถขยายลึกขึ้นได้หากขอ แต่เชื่อผม มันจำเป็น เมื่อคุณระบุคอมมิตที่มีปัญหาแล้ว คุณสามารถ revert หรือ rebase มันเข้าไปใน merge
อย่า squash upstream updates
นักพัฒนาใหม่หลายคนถูก引诱ไปทำสิ่งนี้ เพราะมัน “สะอาดกว่า” และ “ง่ายกว่า” ในการจัดการ นี่เลวร้ายสำหรับหลายเหตุผล:
- การเป็นเจ้าของ (Authorship) ถูกสูญเสีย ไม่ยุติธรรมต่อนักพัฒนาคนอื่นที่จะให้เครดิตของพวกเขาถูกทิ้ง
- Bisecting เป็นไปไม่ได้ หากคุณ squash ชุดคอมมิตและมีปัญหาในชุดนั้น ไม่可能ที่จะบอกว่าคอมมิตไหนก่อให้เกิดปัญหาใน squash
- Future cherry-picks ยากขึ้น หากคุณต้อง rebase ด้วย squashed series มันยาก/เป็นไปไม่ได้ที่จะบอกว่าข้อขัดแย้งมาจากไหน
สมัครรับ Linux Kernel mailing list สำหรับอัปเดตทันเวลา
เพื่อรับการแจ้งเตือนทุกครั้งที่มี upstream update ให้สมัคร the linux-kernel-announce list นี่จะให้คุณได้อีเมลทุกครั้งที่ kernel ใหม่ถูกปล่อยออกมา ดังนั้นคุณสามารถอัปเดตและ push ให้เร็วที่สุด