14.2.2.1. การแช่แข็งสคริปต์ลงในเฟิร์มแวร์#
โมดูล frozen คือไฟล์ .py ที่ถูกคอมไพล์เป็น bytecode และลิงก์เข้าไปในเฟิร์มแวร์ image ณ เวลาสร้าง (build time) รันไทม์จะนำเข้าโมดูล frozen โดยตรงจากแฟลช โดยไม่ต้องค้นหาจากระบบไฟล์บนดิสก์เลย สำหรับผลิตภัณฑ์ที่จัดส่งแล้ว นี่คือตำแหน่งที่เหมาะสมสำหรับโค้ดแอปพลิเคชัน: ผู้ใช้ปลายทางไม่สามารถลบออกได้ ไฟล์ .py เก่าบน SD card ไม่สามารถแทนที่ได้ และกล้องจะรันโค้ดเดิมทุกครั้งที่บูต ไม่ว่าไดรฟ์จะมีอะไรอยู่หรือไม่ก็ตาม
หน้านี้อธิบายลำดับการเริ่มต้น (startup sequence) ที่กล้องปฏิบัติตาม รวมถึงวิธีที่ manifest.py และคำสั่ง freeze ฝังแอปพลิเคชันเข้าสู่การสร้าง
14.2.2.1.1. ลำดับการเริ่มต้น#
สิ่งที่รันและเมื่อไหร่ เมื่อกล้องออกจาก reset:
บูตโหลดเดอร์ การเปิดเครื่องจะเข้าสู่ช่วง DFU สั้น ๆ ที่ IDE ใช้ในการส่งอัปเดตเฟิร์มแวร์ ช่วงนี้จะปิดลงหลังจากไม่กี่วินาที และบูตโหลดเดอร์จะส่งต่อให้ MicroPython สคริปต์ที่รันอยู่สามารถกลับเข้าสู่ช่วงนี้ได้ตามต้องการโดยเรียก
machine.bootloader()การเริ่มต้นระบบไฟล์ frozen ก่อนที่โค้ดแอปพลิเคชันใด ๆ จะรัน รันไทม์จะเตรียมระบบไฟล์ให้พร้อม โดยแฟลชภายในถูกเมาท์ที่
/flash(และฟอร์แมตว่างเปล่าหากไม่มีข้อมูลอยู่) หาก SD card มีอยู่ และ ไม่มีไฟล์เครื่องหมายSKIPSDบนแฟลชภายใน SD card จะถูกเมาท์ที่/sdcardROMFS เมื่อการสร้างรวมไว้ จะถูกเมาท์อัตโนมัติที่/romไดเรกทอรีทำงานถูกตั้งเป็นไดเรกทอรีบูต (/sdcardหาก card เมาท์แล้ว หรือ/flashหากไม่ได้เมาท์) และsys.pathจะถูกเติมด้วย/flash,/flash/lib,/sdcard,/sdcard/lib,/rom, และ/rom/libการตั้งค่าที่อยู่ในแฟลชจัดการโดยโมดูล frozen ที่ชื่อ_boot.py-- เป็นโครงสร้างพื้นฐานของ port และ board ไม่ใช่ hook ของแอปพลิเคชัน แอปพลิเคชันไม่ควรปรับแต่ง_boot.py; การสร้างเป็นผู้ดูแล การวางSKIPSDลงบนแฟลชจาก IDE คือวิธีที่รองรับเพื่อให้กล้องบูตจากแฟลชภายในแทน SD cardการตั้งค่าก่อน REPL
boot.pyรันทุก soft reset -- การบูตเย็น,Ctrl-Dจาก REPL, เมื่อสคริปต์ที่รันคืนค่า, และการกู้คืนจาก watchdog -- ก่อนที่ REPL จะสามารถเข้าถึงได้ หน้าที่คือเตรียมสภาพแวดล้อมที่ระบบส่วนที่เหลือใช้: การตั้งค่าประเภทที่ REPL, แอปพลิเคชัน, และเครื่องมือกู้คืนต้องการให้พร้อมใช้งาน ไม่ใช่ที่อยู่ของแอปพลิเคชันเองmain.pyคือจุดเริ่มต้นของแอปพลิเคชันลูปหลัก
main.pyคือลูปหลักของแอปพลิเคชัน รันครั้งเดียวเมื่อบูตเย็น หลังจากboot.pyทันที ไม่ รันซ้ำเมื่อ soft reset ในภายหลัง -- กล้องจะตกลงสู่ REPL แทน ความไม่สมมาตรนี้มีความสำคัญในการพัฒนา (Ctrl-D จะตกลงสู่ REPL โดยไม่รันลูปซ้ำ ทำให้นักพัฒนาตรวจสอบสถานะได้) แต่ไม่สำคัญในการผลิต: กล้องที่ใช้งานจริงรับ power-on, watchdog, และ hard reset ซึ่งล้วนเป็น hardware reset ที่เข้าสู่เส้นทางบูตเย็นและรันmain.pyอีกครั้ง
14.2.2.1.2. การแช่แข็งเข้าสู่เฟิร์มแวร์#
ชุดโมดูล frozen ของบอร์ดถูกประกาศใน boards/<TARGET>/manifest.py ในต้นไม้เฟิร์มแวร์ manifest คือไฟล์ Python ขนาดเล็กที่เรียกใช้คำสั่งไม่กี่อย่าง:
freeze("$(OMV_LIB_DIR)/", "foo.py")-- ฝังfoo.pyไฟล์เดียวเข้าสู่การสร้างpackage("mylib", base_path="...")-- ฝัง Python package หลายไฟล์โดยรักษาโครงสร้างไดเรกทอรีภายใต้ base path ที่กำหนดinclude("...")-- ดึงไฟล์ manifest อื่นเข้ามา บอร์ด manifests ใช้สิ่งนี้เพื่อแบ่งปันชุดโมดูลร่วมกันrequire("logging")-- ดึงโมดูลmicropython-libupstream ที่มีชื่อมาตามชื่อ
manifest แอปพลิเคชันขั้นต่ำจะเพิ่มบรรทัด freeze หนึ่งบรรทัดต่อสคริปต์ระดับบนสุด และบรรทัด package หนึ่งบรรทัดต่อ package ที่แอปพลิเคชันพึ่งพา
14.2.2.1.2.1. ตำแหน่งของซอร์สโค้ด#
ซอร์สโค้ดแอปพลิเคชันอยู่ใน scripts/libraries/ ในต้นไม้เฟิร์มแวร์ ข้างๆ โมดูลที่การสร้างแช่แข็งไว้แล้ว ตัวแปร manifest $(OMV_LIB_DIR) ขยายไปยังเส้นทางนั้น ทำให้รายการ manifest สั้น การแก้ไข manifest เป็นการดำเนินการภายใน tree อยู่แล้ว ดังนั้นการเก็บซอร์สใน tree ช่วยหลีกเลี่ยงการจัดการ repo โปรเจกต์แยกต่างหากในการแก้ไขเส้นทาง
โครงสร้างทั่วไปสำหรับแอปพลิเคชันที่จัดส่ง main.py ไฟล์เดียวพร้อม package สนับสนุน:
scripts/libraries/
main.py
my_lib/
__init__.py
helpers.py
และใน boards/<TARGET>/manifest.py ของบอร์ด บรรทัด freeze หนึ่งบรรทัดสำหรับสคริปต์ และบรรทัด package หนึ่งบรรทัดสำหรับ package:
freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")
สคริปต์ไฟล์เดียว -- main.py ที่นี่ แต่กฎเดียวกันใช้กับ boot.py หรือ helper แบบ standalone ใด ๆ -- ใช้ freeze Package หลายไฟล์ใช้ package การเพิ่มสคริปต์อื่นคือบรรทัด freeze อีกหนึ่งบรรทัด; การเพิ่ม package อื่นคือบรรทัด package อีกหนึ่งบรรทัด
14.2.2.1.2.2. การสร้างและการแฟลช#
เมื่อ manifest พร้อมแล้ว ให้สร้างเฟิร์มแวร์ตามที่ บทเฟิร์มแวร์ อธิบายไว้:
make -j$(nproc) -C lib/micropython/mpy-cross # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET> # builds the firmware
ผลลัพธ์จะอยู่ใน build/<TARGET>/bin/
build/<TARGET>/bin/
firmware.bin # flash through the IDE
romfs0.img # flash through the IDE in a separate step
การแฟลช .bin และ .img ผ่าน IDE จะทำให้กล้องมีแอปพลิเคชันที่เป็นส่วนหนึ่งของการสร้าง
ลำดับการเริ่มต้นข้างต้นคือสิ่งที่ทำให้การฝังมีประสิทธิภาพ: รันไทม์แก้ไข boot.py และ main.py ไปยังสำเนา frozen ก่อนที่จะตรวจสอบระบบไฟล์ ดังนั้นกล้องที่จัดส่งจะรันโค้ดของการสร้างแม้ว่า SD card จะมี boot.py เก่าจากการพัฒนาก็ตาม
14.2.2.1.2.3. ลำดับการค้นหา#
ความหมายของการแทนที่ แตกต่างกัน สำหรับเส้นทางการรัน boot.py / main.py และสำหรับคำสั่ง import ทั่วไป การรู้ว่าสิ่งใดเป็นอะไรมีความสำคัญทั้งในการผลิตและการพัฒนา:
สำหรับ
boot.pyและmain.py: รันไทม์ค้นหาสำเนา frozen ก่อน แล้วจึงค้นหาระบบไฟล์boot.pyfrozen ไม่สามารถถูกแทนที่โดยการวางบน SD card ได้ -- ผู้ที่ถือกล้องไม่สามารถเปลี่ยน entry point ได้โดยไม่ต้องแฟลชใหม่สำหรับ
import foo: รันไทม์ค้นหาsys.pathก่อน -- ซึ่งครอบคลุม/flash,/sdcard,/rom, และไดเรกทอรีย่อยlibของแต่ละ -- จากนั้นค้นหาโมดูล frozenfoo.pyที่มีชื่อเดียวกันบนแฟลชหรือ SD จะ แทนที่foofrozen การนี้คือความสะดวกในการพัฒนา: วางโมดูลที่แก้ไขแล้วบนการ์ด soft-reset แล้วเห็นการเปลี่ยนแปลงโดยไม่ต้องแฟลชใหม่
ผลิตภัณฑ์ที่จัดส่งที่ต้องการปิดกั้นพฤติกรรมที่ระบบไฟล์แทนที่ frozen สำหรับ imports สามารถล้าง sys.path ตั้งแต่ต้นใน boot.py
import sys
sys.path.clear()
เมื่อ sys.path ว่างเปล่า imports ทั้งหมดจะแก้ไขจากโมดูล frozen เท่านั้น ไม่มีสิ่งใดบนแฟลช, SD, หรือ ROMFS สามารถบดบังได้
14.2.2.1.2.4. ปัญหา asset#
การแช่แข็งเหมาะสำหรับโค้ด แต่ ไม่เหมาะ สำหรับ asset ไบนารีขนาดใหญ่: ไฟล์โมเดล machine learning, ตารางป้ายกำกับ, การกำหนดค่า JSON, เทมเพลตภาพ การฝังสิ่งเหล่านี้เป็น Python literals จะทำให้ซอร์สบวมขึ้น คอมไพล์ช้า และเสียพื้นที่ bytecode container บนข้อมูลที่ interpreter จะต้องอ่านดิบอยู่ดี หน้า การสร้าง ROMFS image อธิบาย read-only flash filesystem ที่เติมเต็มช่องว่างนี้