簡介
MicroPython是 Python 3 語言 的精簡高效實作 ,包括Python标準庫的一小部分,經過優化可在微控制器和受限環境中運作。
MicroPython包含了諸如互動式提示,任意精度整數,關閉,清單解析,生成器,異常處理等進階功能。 足夠精簡,适合運作在隻有256k的代碼空間和16k的RAM的晶片上。
MicroPython旨在盡可能與普通Python相容,讓您輕松将代碼從桌面傳輸到微控制器或嵌入式系統。
應用展望
金融智能卡COS系統利用JavaCard虛拟機實作了APPLET的跨平台應用。但是JavaCard标準規範由Oracle公司定制,具有一定的License費用。國内COS廠商每年都需要花費幾千萬元來擷取JavaCard标準的使用權。為了減少這筆開銷,COS廠商開始尋找新的技術來取代JavaCard虛拟機,比如Native方案等,但這雖然可以在一定的程度上解決了虛拟機的作用,但對于跨平台的應用實作還是無法徹底解決。
物聯網應用碎片化和安全性問題一直是阻礙其發展的主要原因,Micropython虛拟機技術理論上可以完全替代JAVACARD虛拟機,但缺少實用場景,并且其安全性也需要進一步證明論證。但從技術本身上看,Micropython虛拟機的确為物聯網的應用開發提供了一種可能,如果有實際産品出現在市場上,經過物聯網應用的進一步孵化,有可能會成為物聯網領域的"JavaCard"。
平頭哥作為物聯網AIoT裝置的基礎設施提供者,于2015年就針對物聯網領域推出MCU級别的TEE安全方案,我們認為Micropython技術應用在TEE裡,有希望成為物聯網安全應用技術的主要組成部分。基于Micropython虛拟機自身的移植性,很快速便捷的在平頭哥基于CK802T安全處理器的Hobbit1-2上運作起來。基于物聯網産品功能單一的特性,我們可以完全去除Micropython技術的一些無用特性,使之資源消耗達到最小,符合資源受限的物聯網裝置。
工作原理
Micropython技術是依賴Byte Code的執行,在編譯階段就将py檔案先轉換成mpy檔案,在通過mpy-tool.py生成Byte Code,Byte Code在執行時會依賴Virtual Machine入口表,找到對應的Module入口,最終找到對應的Funcion binary code執行。其中所有的Function都通過Dictionary的形式存儲,而每一個Dictionary都有自己的QSTR,Micropython有buildin的QSTR和使用者擴充的QSTR。具體流程可參考如下圖一。

圖一
QSTR的定義
QDEF(MP_QSTR___main__, (const byte*)"\x8e\x13\x08" "__main__")
其中x8ex13是dbj2 hash算法計算出來,x08是QSTR "main"的長度
def dbj2_hash(qstr, bytes_hash):
hash = 5381
for b in qstr:
hash = (hash * 33) ^ b
# Make sure that valid hash is never zero, zero means "hash not com puted"
return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1
通過dbj2 hash值和子串長度作為一個QSTR的ID。
- Build in QSTR
Micropython支援常用的build in的QSTR,包括基本整形類型和運算符等。比如
QDEF(MP_QSTR_ptr32, (const byte*)"\xb2\xca\x05" "ptr32")
QDEF(MP_QSTR_s32i, (const byte*)"\x3e\x34\x04" "s32i")
QDEF(MP_QSTR_float, (const byte*)"\x35\x44\x05" "float")
QDEF(MP_QSTR_add, (const byte*)"\x44\x32\x03" "add")
QDEF(MP_QSTR_sub, (const byte*)"\x21\x8d\x03" "sub")
QDEF(MP_QSTR_xor, (const byte*)"\x20\x93\x03" "xor")
QDEF(MP_QSTR_max, (const byte*)"\xb1\x43\x03" "max")
QDEF(MP_QSTR_min, (const byte*)"\xaf\x42\x03" "min")
QDEF(MP_QSTR___eq__, (const byte*)"\x71\x3e\x06" "__eq__")
QDEF(MP_QSTR___ge__, (const byte*)"\xa7\x46\x06" "__ge__")
QDEF(MP_QSTR___gt__, (const byte*)"\xb6\x82\x06" "__gt__")
- Extend QSTR
除了常用的Build in QSTR外,Micropython還支援擴充的QSTR,進而添加使用者自己的應用功能。比如
// 添加mbedtls udp socket接口
QDEF(MP_QSTR_uselect, (const byte*)"\x58\x8e\x07" "uselect")
QDEF(MP_QSTR_usocket, (const byte*)"\x75\x00\x07" "usocket")
QDEF(MP_QSTR_ussl, (const byte*)"\x1c\xf2\x04" "ussl")
// 添加http socket服務
QDEF(MP_QSTR_uwebsocket, (const byte*)"\xe5\x33\x0a" "uwebsocket")
// 添加密碼學算法服務
QDEF(MP_QSTR_ucryptolib, (const byte*)"\x34\xda\x0a" "ucryptolib")
// 添加随機數服務
QDEF(MP_QSTR_urandom, (const byte*)"\xab\xae\x07" "urandom")
// 添加Json包服務
QDEF(MP_QSTR_ujson, (const byte*)"\xe8\x30\x05" "ujson")
QSTR的生成
無論是Build in QSTR還是Extend QSTR, 都是通過makemodulesdefs.py, makeqstrdefs.py, makeqstrdata.py腳本生成,存放在qstrdefs.generated.h檔案裡。使用者自行擴充的QSTR是否添加成功,可以通過在QSTR AUTO GENERATOR後通過檢視該檔案。
ByteStream的生成
Python源檔案首先經過mpy_cross.elf工具轉成mpy檔案,例如Top.py轉成的Top.mpy二進制如下:
紅色部分就是Top檔案功能可執行代碼,mpy_tool.py工具會提取該功能可執行代碼生成 frozen mpy的C語言檔案,與該Top.py對應的的ByteStream數組如下:
通過比較,fun_data_Top__lt_module_gt__process_apdu的數組内容實際上就是Top.py檔案的功能可執行代碼。利用mpy_tool.py工具可以将Python語言腳本轉換成C語言數組,而該數組内容實際上就是該Python檔案的功能可執行代碼,也稱為ByteStream。
一旦Python檔案調用另一個python檔案,mpy_tool.py工具的做法是通過叫做mp_raw_code_t的資料接口将不同的python檔案調用關系連接配接起來,例如如下mp_raw_code_t的數組将Top.py和Applet_AID1234567890.py檔案連接配接起來,其源于Top.py檔案調用了Applet_AID1234567890.py檔案裡的process_apdu接口。
Virutal Machine Entry Table
- Byte Code定義
Name | Value |
---|---|
MP_BC_LOAD_CONST_FALSE | 0x10 |
MP_BC_LOAD_CONST_NONE | 0x11 |
MP_BC_LOAD_CONST_TRUE | 0x12 |
MP_BC_LOAD_CONST_SMALL_INT | 0x14 |
MP_BC_LOAD_CONST_STRING | 0x16 |
MP_BC_LOAD_CONST_OBJ | 0x17 |
MP_BC_LOAD_NAME | 0x1b |
MP_BC_LOAD_ATTR | 0x1d |
MP_BC_STORE_NAME | 0x24 |
MP_BC_STORE_ATTR | 0x26 |
MP_BC_POP_JUMP_IF_TRUE | 0x36 |
MP_BC_POP_JUMP_IF_FALSE | 0x37 |
MP_BC_BUILD_MAP | 0x53 |
MP_BC_BUILD_SET | 9x56 |
MP_BC_YIELD_VALUE | 0x5d |
MP_BC_YIELD_FROM | 0x5e |
MP_BC_MAKE_FUNCTION | 0x60 |
MP_BC_MAKE_FUNCTION_DEFARGS | 0x61 |
MP_BC_CALL_FUNCTION | 0x64 |
MP_BC_CALL_FUNCTION_VAR_KW | 0x65 |
MP_BC_CALL_METHOD | 0x66 |
MP_BC_CALL_METHOD_VAR_KW | 0x67 |
MP_BC_IMPORT_NAME | 0x68 |
MP_BC_IMPORT_FROM | 0x69 |
MP_BC_IMPORT_STAR | 0x6a |
MP_BC_LOAD_CONST_SMALL_INT_MULTI | 0x70 |
MP_BC_LOAD_FAST_MULTI | 0xb0 |
MP_BC_UNARY_OP_MULTI | 0xd0 |
MP_BC_BINARY_OP_MULTI | 0xd7 |
上述表格定義了部分Byte Code,這些Byte Code在mpy_tool.py工具裡同樣定義了一套。用于将Python檔案轉換成Byte Stream數組,在執行Byte Stream時,Micropython會解析其中的Byte Code, 執行相應的Byte Code處理服務函數。在執行Byte Code處理服務函數裡,根據QSTR在字典資料結構裡的查找到對應的C語言接口,進而實作真正的python執行腳本功能。
MicroPython SDK
SDK裡的主要部件如下:
- py/ – the core Python implementation, including compiler, runtime, and
- library.
- mpy-cross/ – the MicroPython cross-compiler which is used to turn scripts
- precompiled bytecode.
- ports/unix/ – a version of MicroPython that runs on Unix.
-
ports/csky/ – a version of MicroPython that runs on the PyBoard and similar
Hobbit1-2 boards.
- tests/ – test framework and test scripts.
- docs/ – user documentation in Sphinx reStructuredText format.
- extmod/ – additional (non-core) modules implemented in C.
- tools/ – various tools, including the pyboard.py module.
- examples/ – a few example Python scripts.
mpy_cross tool
Micropython的移植首先需要編譯生成MicroPython cross-compiler工具,該工具可以将Python 源檔案預編譯成mpy檔案。
$ cd mpy-cross
$ make
如何建立一個Python對象和方法
該執行個體建立了了一個名為example的python對象,同時也支援add_ints的一個方法。
- Step 1
建立一個example.c的檔案, 包含python系統頭
檔案。
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
- Step 2
定義add_ints 方法。
STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
// Extract the ints from the micropython input objects
int a = mp_obj_get_int(a_obj);
int b = mp_obj_get_int(b_obj);
// Calculate the addition and convert to MicroPython object.
return mp_obj_new_int(a + b);
}
- Step 3
定義example 對add_ints方法的引用。
STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
- Step 4
定義example對象的屬性,利用key/value方式作為example對象的入口查找方式。
STATIC const mp_rom_map_elem_t example_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
};
STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
- Step 5
定義example對象。
const mp_obj_module_t example_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&example_module_globals,
};
- Step 6
注冊example對象到MicroPython對象庫裡。
MP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);
- Step 7
寫python腳本對example對象的通路
import example
print(example.add_ints(1, 3))
總結
Micrypython為嵌入式系統的跨平台應用開發提供了一種可能,特别是資源受限的物聯網裝置。利用其跨平台的特性,能夠快速的移植應用到嵌入式平台上。和Javacard相比,其安全性沒有得到證明,像Byte Code的定義都沒有形成标準規範,對Global Platform的SE規範沒有支援,這些都需要進一步的完善。
原文作者:cui_632
點選檢視原文